From 76c67ff4ac23669d07b803e8519ad66c456fd744 Mon Sep 17 00:00:00 2001 From: Doonv <58695417+doonv@users.noreply.github.com> Date: Sat, 30 Dec 2023 17:11:24 +0200 Subject: [PATCH] Polishing and stuff --- examples/custom_functions.rs | 13 ++-- src/builtin_parser.rs | 16 ++++- src/builtin_parser/number.rs | 57 +++++++++++----- src/builtin_parser/parser.rs | 1 + src/builtin_parser/runner.rs | 12 ++-- src/builtin_parser/runner/error.rs | 14 +++- src/builtin_parser/runner/value.rs | 105 +++++++++++++++-------------- src/ui.rs | 17 +++-- 8 files changed, 150 insertions(+), 85 deletions(-) diff --git a/examples/custom_functions.rs b/examples/custom_functions.rs index 3e8d83d..0d7c410 100644 --- a/examples/custom_functions.rs +++ b/examples/custom_functions.rs @@ -5,7 +5,7 @@ use bevy::{ prelude::*, }; use bevy_dev_console::{ - builtin_parser::{Environment, RunError, Spanned, StrongRef, Value}, + builtin_parser::{Environment, RunError, Spanned, StrongRef, Value, Number}, prelude::*, register, }; @@ -21,7 +21,7 @@ fn time_since_epoch() { } /// Function with parameters and return value -fn add(num1: f64, num2: f64) -> f64 { +fn add(num1: Spanned, num2: Spanned) -> Result { num1 + num2 } @@ -37,24 +37,25 @@ fn print_debug_info(value: Spanned) { struct MyCounter(u32); /// Function with [`World`] -fn increment_global_counter(world: &mut World) -> f64 { +fn increment_global_counter(world: &mut World) -> u32 { world.resource_scope(|_, mut counter: Mut| { counter.0 += 1; - counter.0 as f64 + counter.0 }) } // Function with reference (Syntax subject to change soon) fn increment_number(number: Spanned>) -> Result<(), RunError> { + let span = number.span; let mut reference = number.value.borrow_mut(); if let Value::Number(number) = &mut *reference { - *number += 1.0; + *number = Number::add(*number, Number::Integer(1), span).unwrap(); Ok(()) } else { Err(RunError::Custom { text: "Oh nooo".to_string(), - span: number.span, + span, }) } } diff --git a/src/builtin_parser.rs b/src/builtin_parser.rs index 2e225cd..ffcb7f0 100644 --- a/src/builtin_parser.rs +++ b/src/builtin_parser.rs @@ -12,12 +12,26 @@ use crate::command::{CommandParser, DefaultCommandParser}; use self::{lexer::TokenStream, parser::parse}; pub(crate) mod lexer; +pub(crate) mod number; pub(crate) mod parser; pub(crate) mod runner; -pub(crate) mod number; +pub use number::*; pub use runner::{environment::Environment, error::RunError, unique_rc::*, Value}; +pub trait SpanExtension { + fn wrap(self, value: T) -> Spanned; + fn join(self, span: Self) -> Self; +} +impl SpanExtension for Span { + fn wrap(self, value: T) -> Spanned { + Spanned { span: self, value } + } + fn join(self, span: Self) -> Self { + self.start..span.end + } +} + /// Wrapper around `T` that stores a [Span] (A location in the source code) #[derive(Debug, Clone)] pub struct Spanned { diff --git a/src/builtin_parser/number.rs b/src/builtin_parser/number.rs index 46f33fa..4b32d30 100644 --- a/src/builtin_parser/number.rs +++ b/src/builtin_parser/number.rs @@ -1,12 +1,18 @@ #![allow(non_camel_case_types)] -use std::{fmt::Display, ops::*, str::FromStr}; +use std::fmt::Display; +use std::ops::*; use bevy::reflect::Reflect; use logos::Span; -use super::{RunError, Spanned}; +use super::{RunError, SpanExtension, Spanned}; +/// An enum for containing any type of number. +/// +/// The [`Integer`](Number::Integer) and [`Float`](Number::Float) types +/// are generic types that then get downcasted when they first interact +/// with a concrete type. (i.e. calling a function, etc) #[derive(Debug, Clone, Copy)] pub enum Number { /// Generic integer that can get downcasted. @@ -46,8 +52,8 @@ impl Number { pub fn kind(&self) -> &'static str { match self { - Number::Float(_) => "{float}", - Number::Integer(_) => "{integer}", + Number::Float(_) => "(float)", + Number::Integer(_) => "(integer)", Number::u8(_) => "u8", Number::u16(_) => "u16", Number::u32(_) => "u32", @@ -82,11 +88,10 @@ impl Display for Number { } macro_rules! impl_op { - ($trait:ident, $fn:ident, $op:tt) => { - impl $trait for Number { - type Output = Result; - fn $fn(self, rhs: Number) -> Self::Output { - match (self, rhs) { + ($fn:ident, $op:tt) => { + impl Number { + pub fn $fn(left: Number, right: Number, span: Span) -> Result { + match (left, right) { (Number::u8(left), Number::u8(right)) => Ok(Number::u8(left $op right)), (Number::u16(left), Number::u16(right)) => Ok(Number::u16(left $op right)), (Number::u32(left), Number::u32(right)) => Ok(Number::u32(left $op right)), @@ -121,18 +126,40 @@ macro_rules! impl_op { (Number::Float(left), Number::Float(right)) => Ok(Number::Float(left $op right)), (Number::f32(left), Number::Float(right)) => Ok(Number::f32(left $op right as f32)), (Number::f64(left), Number::Float(right)) => Ok(Number::f64(left $op right as f64)), - _ => todo!() + _ => Err(RunError::IncompatibleNumberTypes { + left: left.kind(), + right: right.kind(), + span + }) } } } }; } -impl_op!(Add, add, +); -impl_op!(Sub, sub, -); -impl_op!(Mul, mul, *); -impl_op!(Div, div, /); -impl_op!(Rem, rem, %); +impl_op!(add, +); +impl_op!(sub, -); +impl_op!(mul, *); +impl_op!(div, /); +impl_op!(rem, %); + +macro_rules! impl_op_spanned { + ($trait:ident, $method:ident) => { + impl $trait for Spanned { + type Output = Result; + fn $method(self, rhs: Self) -> Self::Output { + let span = self.span.join(rhs.span); + + Number::$method(self.value, rhs.value, span) + } + } + }; +} + +impl_op_spanned!(Add, add); +impl_op_spanned!(Sub, sub); +impl_op_spanned!(Mul, mul); +impl_op_spanned!(Rem, rem); impl Number { pub fn neg(self, span: Span) -> Result { diff --git a/src/builtin_parser/parser.rs b/src/builtin_parser/parser.rs index c574b76..b080e89 100644 --- a/src/builtin_parser/parser.rs +++ b/src/builtin_parser/parser.rs @@ -266,6 +266,7 @@ fn parse_primary( if let Some(Function { argument_count, .. }) = environment.get_function(tokens.slice()) { + dbg!(argument_count); let name = tokens.slice().to_string(); let start = tokens.span().start; let mut arguments = Vec::new(); diff --git a/src/builtin_parser/runner.rs b/src/builtin_parser/runner.rs index a2e53dc..29a98fe 100644 --- a/src/builtin_parser/runner.rs +++ b/src/builtin_parser/runner.rs @@ -14,7 +14,7 @@ use self::{ use super::{ parser::{Ast, Expression, Operator}, - Spanned, + Number, SpanExtension, Spanned, }; use bevy::{ prelude::*, @@ -291,11 +291,11 @@ fn eval_expression( match (left, right) { (Value::Number(left), Value::Number(right)) => Ok(Value::Number(match operator { - Operator::Add => (left + right)?, - Operator::Sub => (left - right)?, - Operator::Mul => (left * right)?, - Operator::Div => (left / right)?, - Operator::Mod => (left % right)?, + Operator::Add => Number::add(left, right, expr.span)?, + Operator::Sub => Number::sub(left, right, expr.span)?, + Operator::Mul => Number::mul(left, right, expr.span)?, + Operator::Div => Number::div(left, right, expr.span)?, + Operator::Mod => Number::rem(left, right, expr.span)?, })), (left, right) => todo!("{left:#?}, {right:#?}"), } diff --git a/src/builtin_parser/runner/error.rs b/src/builtin_parser/runner/error.rs index 0d05178..0da5c7e 100644 --- a/src/builtin_parser/runner/error.rs +++ b/src/builtin_parser/runner/error.rs @@ -38,6 +38,11 @@ pub enum RunError { }, CannotMoveOutOfResource(Spanned), CannotNegateUnsignedInteger(Spanned), + IncompatibleNumberTypes { + left: &'static str, + right: &'static str, + span: Span + }, } impl RunError { @@ -58,7 +63,8 @@ impl RunError { IncompatibleReflectTypes { span, .. } => vec![span.clone()], EnumVariantNotFound { span, .. } => vec![span.clone()], CannotMoveOutOfResource(Spanned { span, .. }) => vec![span.clone()], - CannotNegateUnsignedInteger(Spanned { span, .. }) => vec![span.clone()] + CannotNegateUnsignedInteger(Spanned { span, .. }) => vec![span.clone()], + IncompatibleNumberTypes { span, .. } => vec![span.clone()], } } pub fn hints(&self) -> Vec { @@ -99,6 +105,12 @@ impl RunError { value.kind() ) .into(), + IncompatibleNumberTypes { + left, + right, + .. + } => format!("Incompatible number types; `{left}` and `{right}` are incompatible.") + .into(), } } } diff --git a/src/builtin_parser/runner/value.rs b/src/builtin_parser/runner/value.rs index 6c1d5ee..980f739 100644 --- a/src/builtin_parser/runner/value.rs +++ b/src/builtin_parser/runner/value.rs @@ -242,6 +242,16 @@ impl From<()> for Value { Value::None } } + +macro_rules! from_t { + (impl $type:ty: $var:ident => $expr:expr) => { + impl From<$type> for Value { + fn from($var: $type) -> Self { + $expr + } + } + }; +} macro_rules! from_number { ($($number:ident),*$(,)?) => { $( @@ -254,45 +264,18 @@ macro_rules! from_number { }; } -from_number!( - u8, - u16, - u32, - u64, - i8, - i16, - i32, - i64, - f32, - f64, -); - -impl From for Value { - fn from(string: String) -> Self { - Value::String(string) - } -} -impl From for Value { - fn from(boolean: bool) -> Self { - Value::Boolean(boolean) - } -} +from_number!(u8, u16, u32, u64, i8, i16, i32, i64, f32, f64); -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(), - ) - } -} +from_t!(impl String: string => Value::String(string)); +from_t!(impl bool: bool => Value::Boolean(bool)); +from_t!(impl Number: number => Value::Number(number)); +from_t!(impl HashMap>>: hashmap => Value::Object(hashmap)); +from_t!(impl HashMap: hashmap => 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; @@ -371,27 +354,45 @@ macro_rules! impl_function_param_for_value { }; } macro_rules! impl_function_param_for_numbers { - ($($number:ident),*$(,)?) => { + ($generic:ident ($($number:ident),*$(,)?)) => { $( - impl_function_param_for_value!(impl $number: Value::Number(Number::$number(number)) => number); + impl FunctionParam for $number { + 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> { + match value.unwrap().value { + Value::Number(Number::$number(value)) => Ok(value), + Value::Number(Number::$generic(value)) => Ok(value as $number), + _ => todo!() + } + } + } + impl TryFrom for $number { + type Error = RunError; + + fn try_from(value: Value) -> Result { + match value { + Value::Number(Number::$number(value)) => Ok(value), + Value::Number(Number::$generic(value)) => Ok(value as $number), + _ => todo!() + } + } + } )* }; } -impl_function_param_for_numbers!( - u8, - u16, - u32, - u64, - i8, - i16, - i32, - i64, - f32, - f64, -); +impl_function_param_for_numbers!(Float(f32, f64)); +impl_function_param_for_numbers!(Integer(u8, u16, u32, u64, i8, i16, i32, i64)); impl_function_param_for_value!(impl bool: Value::Boolean(boolean) => boolean); +impl_function_param_for_value!(impl Number: Value::Number(number) => number); 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) => { diff --git a/src/ui.rs b/src/ui.rs index e79af65..6d33ec1 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -115,8 +115,18 @@ pub(crate) fn render_ui( .auto_shrink([false, true]) .show(ui, |ui| { ui.vertical(|ui| { + let mut command_index = 0; + for (id, (message, is_new)) in state.log.iter_mut().enumerate() { - add_log(ui, id, message, is_new, &mut hints, &config); + add_log( + ui, + id, + message, + is_new, + &mut hints, + &config, + &mut command_index, + ); } }); }); @@ -130,14 +140,13 @@ fn add_log( is_new: &mut bool, hints: &mut CommandHints, config: &ConsoleConfig, + command_index: &mut usize, ) { - let mut command_index = 0; - ui.push_id(id, |ui| { let time_utc = system_time_to_chrono_utc(event.time); let time: DateTime = time_utc.into(); - let text = format_line(time, config, event, *is_new, &mut command_index, hints); + let text = format_line(time, config, event, *is_new, command_index, hints); let label = ui.label(text); if *is_new {