Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Additional number types (and half-assed type inference) #5

Merged
merged 9 commits into from
Jan 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ tracing-log = "0.2.0"
tracing-subscriber = "0.3.18"
web-time = "0.2.4"

# built-in parser
# builtin-parser feature
logos = { version = "0.13.0", optional = true }

[target.'cfg(target_os = "android")'.dependencies]
Expand Down
15 changes: 9 additions & 6 deletions examples/custom_functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use bevy::{
prelude::*,
};
use bevy_dev_console::{
builtin_parser::{Environment, RunError, Spanned, StrongRef, Value},
builtin_parser::{Environment, Number, RunError, Spanned, StrongRef, Value},
prelude::*,
register,
};
Expand All @@ -20,7 +20,9 @@ fn time_since_epoch() {
info!("The unix epoch was {} seconds ago", time.as_secs());
}

/// Function with parameters and return value
/// Function with parameters and return value.
///
/// Note that this will cause an error if an integer to passed to this function.
fn add(num1: f64, num2: f64) -> f64 {
num1 + num2
}
Expand All @@ -37,24 +39,25 @@ fn print_debug_info(value: Spanned<Value>) {
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<MyCounter>| {
counter.0 += 1;

counter.0 as f64
counter.0
})
}

// Function with reference (Syntax subject to change soon)
fn increment_number(number: Spanned<StrongRef<Value>>) -> 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,
})
}
}
Expand Down
18 changes: 18 additions & 0 deletions src/builtin_parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,29 @@ 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 use number::*;
pub use runner::{environment::Environment, error::RunError, unique_rc::*, Value};

/// Additonal traits for span.
pub trait SpanExtension {
/// Wrap this value with a [`Spanned`].
fn wrap<T>(self, value: T) -> Spanned<T>;
/// Combine two [`Span`]s into one.
fn join(self, span: Self) -> Self;
}
impl SpanExtension for Span {
fn wrap<T>(self, value: T) -> Spanned<T> {
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<T> {
Expand Down
30 changes: 22 additions & 8 deletions src/builtin_parser/lexer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,22 @@ pub enum Token {
#[regex("[a-zA-Z_][a-zA-Z0-9_]*")]
Identifer,

#[regex("[0-9]+")]
Number,
#[regex(r#"[0-9]+"#)]
IntegerNumber,
#[regex(r#"[0-9]*\.?[0-9]*"#)]
FloatNumber,

#[token("i8")]
#[token("i16")]
#[token("i32")]
#[token("i64")]
#[token("u8")]
#[token("u16")]
#[token("u32")]
#[token("u64")]
#[token("f32")]
#[token("f64")]
NumberType,
}

/// A wrapper for the lexer which provides token peeking and other helper functions
Expand Down Expand Up @@ -138,7 +152,7 @@ impl<'a> TokenStream<'a> {
}

/// Get a [`str`] slice of the next [`Token`].
pub fn _peek_slice(&self) -> &str {
pub fn peek_slice(&self) -> &str {
self.lexer.slice()
}

Expand Down Expand Up @@ -171,27 +185,27 @@ mod tests {

#[test]
fn var_assign() {
let mut lexer = TokenStream::new("x = 1 + 2 - 30");
let mut lexer = TokenStream::new("x = 1 + 2 - 30.6");

assert_eq!(lexer.next(), Some(Ok(Token::Identifer)));
assert_eq!(lexer.slice(), "x");

assert_eq!(lexer.next(), Some(Ok(Token::Equals)));
assert_eq!(lexer.slice(), "=");

assert_eq!(lexer.next(), Some(Ok(Token::Number)));
assert_eq!(lexer.next(), Some(Ok(Token::IntegerNumber)));
assert_eq!(lexer.slice(), "1");

assert_eq!(lexer.next(), Some(Ok(Token::Plus)));
assert_eq!(lexer.slice(), "+");

assert_eq!(lexer.next(), Some(Ok(Token::Number)));
assert_eq!(lexer.next(), Some(Ok(Token::IntegerNumber)));
assert_eq!(lexer.slice(), "2");

assert_eq!(lexer.next(), Some(Ok(Token::Minus)));
assert_eq!(lexer.slice(), "-");

assert_eq!(lexer.next(), Some(Ok(Token::Number)));
assert_eq!(lexer.slice(), "30");
assert_eq!(lexer.next(), Some(Ok(Token::FloatNumber)));
assert_eq!(lexer.slice(), "30.6");
}
}
217 changes: 217 additions & 0 deletions src/builtin_parser/number.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
use std::fmt::Display;
use std::ops::*;

use bevy::reflect::Reflect;
use logos::Span;

use super::{RunError, SpanExtension, Spanned};

/// An enum that contains 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)]
#[allow(missing_docs, non_camel_case_types)]
pub enum Number {
/// Generic integer that can get downcasted.
Integer(i128),
/// Generic float that can get downcasted to a [`f64`] and [`f32`]
Float(f64),

u8(u8),
u16(u16),
u32(u32),
u64(u64),
i8(i8),
i16(i16),
i32(i32),
i64(i64),
f32(f32),
f64(f64),
}

impl Number {
/// Converts this into a [`Box<dyn Reflect>`](Reflect).
pub fn reflect(self) -> Box<dyn Reflect> {
match self {
Number::u8(number) => Box::new(number),
Number::u16(number) => Box::new(number),
Number::u32(number) => Box::new(number),
Number::u64(number) => Box::new(number),
Number::i8(number) => Box::new(number),
Number::i16(number) => Box::new(number),
Number::i32(number) => Box::new(number),
Number::i64(number) => Box::new(number),
Number::f32(number) => Box::new(number),
Number::f64(number) => Box::new(number),
Number::Integer(_) => todo!(),
Number::Float(_) => todo!(),
}
}

/// Returns a [`&'static str`](str) represents the kind of the number.
pub fn kind(&self) -> &'static str {
match self {
Number::Float(_) => "a float",
Number::Integer(_) => "an integer",
Number::u8(_) => "a u8",
Number::u16(_) => "a u16",
Number::u32(_) => "a u32",
Number::u64(_) => "a u64",
Number::i8(_) => "a i8",
Number::i16(_) => "a i16",
Number::i32(_) => "a i32",
Number::i64(_) => "a i64",
Number::f32(_) => "a f32",
Number::f64(_) => "a f64",
}
}
}

impl Display for Number {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Number::Float(number) => write!(f, "{number} (float)"),
Number::Integer(number) => write!(f, "{number} (integer)"),
Number::u8(number) => write!(f, "{number} (u8)"),
Number::u16(number) => write!(f, "{number} (u16)"),
Number::u32(number) => write!(f, "{number} (u32)"),
Number::u64(number) => write!(f, "{number} (u64)"),
Number::i8(number) => write!(f, "{number} (i8)"),
Number::i16(number) => write!(f, "{number} (i16)"),
Number::i32(number) => write!(f, "{number} (i32)"),
Number::i64(number) => write!(f, "{number} (i64)"),
Number::f32(number) => write!(f, "{number} (f32)"),
Number::f64(number) => write!(f, "{number} (f64)"),
}
}
}

macro_rules! impl_op {
($fn:ident, $op:tt) => {
impl Number {
#[doc = concat!("Performs the `", stringify!($op), "` calculation.")]
pub fn $fn(left: Number, right: Number, span: Span) -> Result<Number, RunError> {
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)),
(Number::u64(left), Number::u64(right)) => Ok(Number::u64(left $op right)),
(Number::i8(left), Number::i8(right)) => Ok(Number::i8(left $op right)),
(Number::i16(left), Number::i16(right)) => Ok(Number::i16(left $op right)),
(Number::i32(left), Number::i32(right)) => Ok(Number::i32(left $op right)),
(Number::i64(left), Number::i64(right)) => Ok(Number::i64(left $op right)),
(Number::f32(left), Number::f32(right)) => Ok(Number::f32(left $op right)),
(Number::f64(left), Number::f64(right)) => Ok(Number::f64(left $op right)),

(Number::Integer(left), Number::u8(right)) => Ok(Number::u8(left as u8 $op right)),
(Number::Integer(left), Number::u16(right)) => Ok(Number::u16(left as u16 $op right)),
(Number::Integer(left), Number::u32(right)) => Ok(Number::u32(left as u32 $op right)),
(Number::Integer(left), Number::u64(right)) => Ok(Number::u64(left as u64 $op right)),
(Number::Integer(left), Number::i8(right)) => Ok(Number::i8(left as i8 $op right)),
(Number::Integer(left), Number::i16(right)) => Ok(Number::i16(left as i16 $op right)),
(Number::Integer(left), Number::i32(right)) => Ok(Number::i32(left as i32 $op right)),
(Number::Integer(left), Number::i64(right)) => Ok(Number::i64(left as i64 $op right)),
(Number::Integer(left), Number::Integer(right)) => Ok(Number::Integer(left $op right)),
(Number::u8(left), Number::Integer(right)) => Ok(Number::u8(left $op right as u8)),
(Number::u16(left), Number::Integer(right)) => Ok(Number::u16(left $op right as u16)),
(Number::u32(left), Number::Integer(right)) => Ok(Number::u32(left $op right as u32)),
(Number::u64(left), Number::Integer(right)) => Ok(Number::u64(left $op right as u64)),
(Number::i8(left), Number::Integer(right)) => Ok(Number::i8(left $op right as i8)),
(Number::i16(left), Number::Integer(right)) => Ok(Number::i16(left $op right as i16)),
(Number::i32(left), Number::Integer(right)) => Ok(Number::i32(left $op right as i32)),
(Number::i64(left), Number::Integer(right)) => Ok(Number::i64(left $op right as i64)),

(Number::Float(left), Number::f32(right)) => Ok(Number::f32(left as f32 $op right)),
(Number::Float(left), Number::f64(right)) => Ok(Number::f64(left as f64 $op right)),
(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)),
_ => Err(RunError::IncompatibleNumberTypes {
left: left.kind(),
right: right.kind(),
span
})
}
}
}
};
}

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<Self> for Spanned<Number> {
type Output = Result<Number, RunError>;
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 {
/// Performs the unary `-` operation.
pub fn neg(self, span: Span) -> Result<Number, RunError> {
match self {
Number::u8(_) => Err(RunError::CannotNegateUnsignedInteger(Spanned {
span,
value: self,
})),
Number::u16(_) => Err(RunError::CannotNegateUnsignedInteger(Spanned {
span,
value: self,
})),
Number::u32(_) => Err(RunError::CannotNegateUnsignedInteger(Spanned {
span,
value: self,
})),
Number::u64(_) => Err(RunError::CannotNegateUnsignedInteger(Spanned {
span,
value: self,
})),
Number::i8(number) => Ok(Number::i8(-number)),
Number::i16(number) => Ok(Number::i16(-number)),
Number::i32(number) => Ok(Number::i32(-number)),
Number::i64(number) => Ok(Number::i64(-number)),
Number::f32(number) => Ok(Number::f32(-number)),
Number::f64(number) => Ok(Number::f64(-number)),
Number::Float(number) => Ok(Number::Float(-number)),
Number::Integer(number) => Ok(Number::Integer(-number)),
}
}
}

macro_rules! from_primitive {
($primitive:ident) => {
impl From<$primitive> for Number {
fn from(value: $primitive) -> Self {
Number::$primitive(value)
}
}
};
}

from_primitive!(u8);
from_primitive!(u16);
from_primitive!(u32);
from_primitive!(u64);
from_primitive!(i8);
from_primitive!(i16);
from_primitive!(i32);
from_primitive!(i64);
from_primitive!(f32);
from_primitive!(f64);
Loading