From a084a0c24742a35e7e6c06dd1d6703f1daddc50d Mon Sep 17 00:00:00 2001 From: Doonv <58695417+doonv@users.noreply.github.com> Date: Tue, 23 Jan 2024 18:17:50 +0200 Subject: [PATCH 1/6] Add Access type for MemberExpressions --- src/builtin_parser.rs | 11 ++- src/builtin_parser/parser.rs | 127 ++++++++++++++++++----------- src/builtin_parser/runner.rs | 70 ++++++++++------ src/builtin_parser/runner/error.rs | 21 ++++- 4 files changed, 153 insertions(+), 76 deletions(-) diff --git a/src/builtin_parser.rs b/src/builtin_parser.rs index 554f094..282c6af 100644 --- a/src/builtin_parser.rs +++ b/src/builtin_parser.rs @@ -47,7 +47,16 @@ pub struct Spanned { /// The value of `T`. pub value: T, } - +impl Spanned { + /// Maps a `Spanned` to `Spanned` by applying a function to a + /// contained `T` value, leaving the [`Span`] value untouched. + pub fn map(self, f: impl Fn(T) -> U) -> Spanned { + Spanned { + span: self.span, + value: f(self.value), + } + } +} impl Default for DefaultCommandParser { fn default() -> Self { Self(Box::new(BuiltinCommandParser)) diff --git a/src/builtin_parser/parser.rs b/src/builtin_parser/parser.rs index 228a260..e02e386 100644 --- a/src/builtin_parser/parser.rs +++ b/src/builtin_parser/parser.rs @@ -64,7 +64,7 @@ pub enum Expression { UnaryOp(Box>), Member { left: Box>, - right: String, + right: Spanned, }, // Statement-like @@ -83,6 +83,28 @@ pub enum Expression { }, } +/// A singular element access within a [`Expression::Member`]. +/// +/// Based on `bevy_reflect`'s `Access`. +#[derive(Debug, Clone)] +pub enum Access { + /// A name-based field access on a struct. + Field(String), + // /// An index-based access on a tuple. + // TupleIndex(usize), + // /// An index-based access on a list. + // ListIndex(usize), +} +impl Access { + /// Returns the kind of [`Access`] as a [string slice](str) with an `a` or `an` prepended to it. + /// Used for more natural sounding error messages. + pub fn natural_kind(&self) -> &'static str { + match self { + Access::Field(_) => "a field access", + } + } +} + impl Expression { pub fn kind(&self) -> &'static str { match self { @@ -385,43 +407,7 @@ fn parse_primary( let expr = parse_primary(tokens, environment)?; Ok(tokens.wrap_span(Expression::Dereference(Box::new(expr)))) } - Some(Ok(Token::IntegerNumber)) => { - if let Some(Ok(Token::NumberType)) = tokens.peek() { - let err_map = |error: std::num::ParseIntError| match error.kind() { - IntErrorKind::PosOverflow => ParseError::PositiveIntOverflow(tokens.span()), - IntErrorKind::NegOverflow => ParseError::NegativeIntOverflow(tokens.span()), - _ => unreachable!("lexer makes sure other errors arent possible"), - }; - let number: Number = match tokens.peek_slice() { - "u8" => Number::u8(tokens.slice().parse().map_err(err_map)?), - "u16" => Number::u16(tokens.slice().parse().map_err(err_map)?), - "u32" => Number::u32(tokens.slice().parse().map_err(err_map)?), - "u64" => Number::u64(tokens.slice().parse().map_err(err_map)?), - "usize" => Number::usize(tokens.slice().parse().map_err(err_map)?), - "i8" => Number::i8(tokens.slice().parse().map_err(err_map)?), - "i16" => Number::i16(tokens.slice().parse().map_err(err_map)?), - "i32" => Number::i32(tokens.slice().parse().map_err(err_map)?), - "isize" => Number::isize(tokens.slice().parse().map_err(err_map)?), - "f32" => Number::f32(tokens.slice().parse().unwrap()), - "f64" => Number::f64(tokens.slice().parse().unwrap()), - _ => unreachable!(), - }; - let start_span = tokens.span().end; - tokens.next(); - - Ok(Spanned { - span: start_span..tokens.span().end, - value: Expression::Number(number), - }) - } else { - let number = Number::Integer(tokens.slice().parse().unwrap()); - - Ok(Spanned { - span: tokens.span(), - value: Expression::Number(number), - }) - } - } + Some(Ok(Token::IntegerNumber)) => parse_number(tokens).map(|s| s.map(Expression::Number)), Some(Ok(Token::FloatNumber)) => { if let Some(Ok(Token::NumberType)) = tokens.peek() { let number: Number = match tokens.peek_slice() { @@ -462,20 +448,65 @@ fn parse_primary( }?; // If theres a dot after the expression, do a member expression: while let Some(Ok(Token::Dot)) = tokens.peek() { - tokens.next(); // skip the dot - expect!(tokens, Token::Identifer); - let right = tokens.slice().to_string(); - expr = Spanned { - span: expr.span.start..tokens.span().end, - value: Expression::Member { - left: Box::new(expr), - right, - }, - }; + tokens.next(); // Skip the dot + match tokens.next() { + Some(Ok(Token::Identifer)) => { + let right = tokens.slice().to_string(); + expr = Spanned { + span: expr.span.start..tokens.span().end, + value: Expression::Member { + left: Box::new(expr), + right: tokens.wrap_span(Access::Field(right)), + }, + }; + } + Some(Ok(Token::LeftBrace)) => {} + _ => todo!(), + } } Ok(expr) } +fn parse_number(tokens: &mut TokenStream) -> Result, ParseError> { + if let Some(Ok(Token::NumberType)) = tokens.peek() { + let err_map = |error: std::num::ParseIntError| match error.kind() { + IntErrorKind::PosOverflow => ParseError::PositiveIntOverflow(tokens.span()), + IntErrorKind::NegOverflow => ParseError::NegativeIntOverflow(tokens.span()), + _ => unreachable!( + "Lexer makes sure other errors aren't possible. Create an bevy_dev_console issue!" + ), + }; + let number: Number = match tokens.peek_slice() { + "u8" => Number::u8(tokens.slice().parse().map_err(err_map)?), + "u16" => Number::u16(tokens.slice().parse().map_err(err_map)?), + "u32" => Number::u32(tokens.slice().parse().map_err(err_map)?), + "u64" => Number::u64(tokens.slice().parse().map_err(err_map)?), + "usize" => Number::usize(tokens.slice().parse().map_err(err_map)?), + "i8" => Number::i8(tokens.slice().parse().map_err(err_map)?), + "i16" => Number::i16(tokens.slice().parse().map_err(err_map)?), + "i32" => Number::i32(tokens.slice().parse().map_err(err_map)?), + "isize" => Number::isize(tokens.slice().parse().map_err(err_map)?), + "f32" => Number::f32(tokens.slice().parse().unwrap()), + "f64" => Number::f64(tokens.slice().parse().unwrap()), + _ => unreachable!(), + }; + let start_span = tokens.span().end; + tokens.next(); + + Ok(Spanned { + span: start_span..tokens.span().end, + value: number, + }) + } else { + let number = Number::Integer(tokens.slice().parse().unwrap()); + + Ok(Spanned { + span: tokens.span(), + value: number, + }) + } +} + fn parse_var_assign( name: Spanned, tokens: &mut TokenStream<'_>, diff --git a/src/builtin_parser/runner.rs b/src/builtin_parser/runner.rs index 02905dd..78a15ad 100644 --- a/src/builtin_parser/runner.rs +++ b/src/builtin_parser/runner.rs @@ -15,7 +15,7 @@ use self::error::RunError; use self::reflection::{object_to_dynamic_struct, CreateRegistration, IntoResource}; use self::unique_rc::{UniqueRc, WeakRef}; -use super::parser::{Ast, Expression, Operator}; +use super::parser::{Access, Ast, Expression, Operator}; use super::{Number, SpanExtension, Spanned}; pub mod environment; @@ -446,15 +446,7 @@ fn eval_expression( loop_count, block, } => todo_error!("for loop {index_name}, {loop_count}, {block:#?}"), - Expression::Member { left, right } => eval_member_expression( - *left, - right, - EvalParams { - world, - environment, - registrations, - }, - ), + Expression::Member { left, right } => todo!(), Expression::UnaryOp(sub_expr) => { let span = sub_expr.span.clone(); let value = eval_expression( @@ -614,24 +606,41 @@ fn eval_path( registrations, }, )?; - match left.value { Path::Variable(variable) => match &*variable.upgrade().unwrap().borrow() { Value::Resource(resource) => { - let mut resource = resource.clone(); - - resource.path.push('.'); - resource.path += &right; - - Ok(left.span.wrap(Path::Resource(resource))) + if let Access::Field(right) = right.value { + let mut resource = resource.clone(); + + resource.path.push('.'); + resource.path += &right; + + Ok(left.span.wrap(Path::Resource(resource))) + } else { + Err(RunError::IncorrectAccessOperation { + span: right.span, + expected_access: &["a field access"], + expected_type: "a resource", + got: right.value, + }) + } } Value::Object(object) | Value::StructObject { map: object, .. } => { - let weak = match object.get(&right) { - Some(rc) => Ok(rc.borrow()), - None => todo_error!(), - }?; - - Ok(left.span.wrap(Path::Variable(weak))) + if let Access::Field(right) = right.value { + let weak = match object.get(&right) { + Some(rc) => Ok(rc.borrow()), + None => todo_error!(), + }?; + + Ok(left.span.wrap(Path::Variable(weak))) + } else { + Err(RunError::IncorrectAccessOperation { + span: right.span, + expected_access: &["a field access"], + expected_type: "an object", + got: right.value, + }) + } } Value::Tuple(tuple) | Value::StructTuple { tuple, .. } => { todo_error!("eval_path only takes in a ident so this doesn't really work") @@ -639,10 +648,19 @@ fn eval_path( value => todo_error!("{value:?}"), }, Path::Resource(mut resource) => { - resource.path.push('.'); - resource.path += &right; + if let Access::Field(right) = right.value { + resource.path.push('.'); + resource.path += &right; - Ok(left.span.wrap(Path::Resource(resource))) + Ok(left.span.wrap(Path::Resource(resource))) + } else { + Err(RunError::IncorrectAccessOperation { + span: right.span, + expected_access: &["a field access"], + expected_type: "a resource", + got: right.value, + }) + } } Path::NewVariable(name) => Err(RunError::VariableNotFound(left.span.wrap(name))), } diff --git a/src/builtin_parser/runner/error.rs b/src/builtin_parser/runner/error.rs index ba51978..3dc815c 100644 --- a/src/builtin_parser/runner/error.rs +++ b/src/builtin_parser/runner/error.rs @@ -3,6 +3,7 @@ use std::borrow::Cow; use logos::Span; use crate::builtin_parser::number::Number; +use crate::builtin_parser::parser::Access; use crate::builtin_parser::Spanned; use crate::command::{CommandHint, CommandHintColor}; @@ -59,9 +60,15 @@ pub enum RunError { CannotReflectReference(Span), CannotReflectResource(Span), EnumVariantTupleFieldNotFound { + span: Span, field_index: usize, variant_name: String, - span: std::ops::Range, + }, + IncorrectAccessOperation { + span: Span, + expected_access: &'static [&'static str], + expected_type: &'static str, + got: Access, }, } @@ -92,6 +99,7 @@ impl RunError { CannotReflectReference(span) => vec![span.clone()], CannotReflectResource(span) => vec![span.clone()], InvalidOperation { span, .. } => vec![span.clone()], + IncorrectAccessOperation { span, .. } => vec![span.clone()], } } /// Returns all the hints for this error. @@ -181,6 +189,17 @@ impl RunError { operation, span: _, } => format!("Invalid operation: Cannot {operation} {left} by {right}").into(), + IncorrectAccessOperation { + expected_access, + expected_type, + got, + span: _, + } => format!( + "{} to access {expected_type} but got {}", + expected_access.join(" and "), + got.natural_kind() + ) + .into(), } } } From 9a99c38841d0019859e17511a257efe1707841c0 Mon Sep 17 00:00:00 2001 From: Doonv <58695417+doonv@users.noreply.github.com> Date: Fri, 26 Jan 2024 18:42:36 +0200 Subject: [PATCH 2/6] Tuple indexing --- src/builtin_parser.rs | 3 + src/builtin_parser/lexer.rs | 4 +- src/builtin_parser/parser.rs | 120 +++++++++++++++++++++++++++-------- src/builtin_parser/runner.rs | 18 +++++- 4 files changed, 113 insertions(+), 32 deletions(-) diff --git a/src/builtin_parser.rs b/src/builtin_parser.rs index 282c6af..238aabb 100644 --- a/src/builtin_parser.rs +++ b/src/builtin_parser.rs @@ -71,6 +71,9 @@ pub struct BuiltinCommandParser; impl CommandParser for BuiltinCommandParser { fn parse(&self, command: &str, world: &mut World) { let mut tokens = TokenStream::new(command); + for a in TokenStream::new(command) { + dbg!(a); + } let environment = world.remove_non_send_resource::().unwrap(); let ast = parse(&mut tokens, &environment); diff --git a/src/builtin_parser/lexer.rs b/src/builtin_parser/lexer.rs index 1c7f638..7456d7a 100644 --- a/src/builtin_parser/lexer.rs +++ b/src/builtin_parser/lexer.rs @@ -38,7 +38,7 @@ pub enum Token { #[token("%")] Modulo, - #[token(".")] + #[token(".", priority = 10)] Dot, #[token("&")] Ampersand, @@ -69,7 +69,7 @@ pub enum Token { #[regex(r#"[0-9]+"#)] IntegerNumber, - #[regex(r#"[0-9]*\.?[0-9]*"#)] + #[regex(r#"[0-9]+\.[0-9]*"#)] FloatNumber, #[token("i8")] diff --git a/src/builtin_parser/parser.rs b/src/builtin_parser/parser.rs index e02e386..72fcf83 100644 --- a/src/builtin_parser/parser.rs +++ b/src/builtin_parser/parser.rs @@ -90,8 +90,8 @@ pub enum Expression { pub enum Access { /// A name-based field access on a struct. Field(String), - // /// An index-based access on a tuple. - // TupleIndex(usize), + /// An index-based access on a tuple. + TupleIndex(usize), // /// An index-based access on a list. // ListIndex(usize), } @@ -100,7 +100,8 @@ impl Access { /// Used for more natural sounding error messages. pub fn natural_kind(&self) -> &'static str { match self { - Access::Field(_) => "a field access", + Access::Field(_) => "a field", + Access::TupleIndex(_) => "a tuple", } } } @@ -176,7 +177,7 @@ fn parse_expression( tokens: &mut TokenStream, environment: &Environment, ) -> Result, ParseError> { - Ok(match tokens.peek() { + match tokens.peek() { Some(Ok(Token::For)) => { let start = tokens.span().start; tokens.next(); @@ -205,35 +206,37 @@ fn parse_expression( let block = parse_block(tokens, environment)?; let end = tokens.span().end; - Spanned { + Ok(Spanned { value: Expression::ForLoop { index_name, loop_count, block, }, span: start..end, - } + }) } Some(Ok(_)) => { let expr = parse_additive(tokens, environment)?; match tokens.peek() { - Some(Ok(Token::Equals)) => parse_var_assign(expr, tokens, environment)?, - _ => expr, + Some(Ok(Token::Equals)) => Ok(parse_var_assign(expr, tokens, environment)?), + _ => Ok(expr), } } Some(Err(FailedToLexCharacter)) => { - return Err(ParseError::FailedToLexCharacter(tokens.peek_span())) + Err(ParseError::FailedToLexCharacter(tokens.peek_span())) } - None => return Err(ParseError::ExpectedMoreTokens(tokens.peek_span())), - }) + None => Err(ParseError::ExpectedMoreTokens(tokens.peek_span())), + } } + fn parse_block(tokens: &mut TokenStream, environment: &Environment) -> Result { expect!(tokens, Token::LeftBracket); let ast = parse(tokens, environment)?; expect!(tokens, Token::RightBracket); Ok(ast) } + fn parse_additive( tokens: &mut TokenStream, environment: &Environment, @@ -460,32 +463,93 @@ fn parse_primary( }, }; } - Some(Ok(Token::LeftBrace)) => {} + Some(Ok(Token::IntegerNumber)) => { + let right = tokens + .slice() + .parse() + .map_err(map_parseint_error(tokens.span()))?; + expr = Spanned { + span: expr.span.start..tokens.span().end, + value: Expression::Member { + left: Box::new(expr), + right: tokens.wrap_span(Access::TupleIndex(right)), + }, + }; + + } _ => todo!(), } } Ok(expr) } +fn map_parseint_error(span: Span) -> impl Fn(std::num::ParseIntError) -> ParseError { + move |error| match error.kind() { + IntErrorKind::PosOverflow => ParseError::PositiveIntOverflow(span.clone()), + IntErrorKind::NegOverflow => ParseError::NegativeIntOverflow(span.clone()), + _ => unreachable!( + "Lexer makes sure other errors aren't possible. Create an bevy_dev_console issue!" + ), + } +} + fn parse_number(tokens: &mut TokenStream) -> Result, ParseError> { if let Some(Ok(Token::NumberType)) = tokens.peek() { - let err_map = |error: std::num::ParseIntError| match error.kind() { - IntErrorKind::PosOverflow => ParseError::PositiveIntOverflow(tokens.span()), - IntErrorKind::NegOverflow => ParseError::NegativeIntOverflow(tokens.span()), - _ => unreachable!( - "Lexer makes sure other errors aren't possible. Create an bevy_dev_console issue!" - ), - }; let number: Number = match tokens.peek_slice() { - "u8" => Number::u8(tokens.slice().parse().map_err(err_map)?), - "u16" => Number::u16(tokens.slice().parse().map_err(err_map)?), - "u32" => Number::u32(tokens.slice().parse().map_err(err_map)?), - "u64" => Number::u64(tokens.slice().parse().map_err(err_map)?), - "usize" => Number::usize(tokens.slice().parse().map_err(err_map)?), - "i8" => Number::i8(tokens.slice().parse().map_err(err_map)?), - "i16" => Number::i16(tokens.slice().parse().map_err(err_map)?), - "i32" => Number::i32(tokens.slice().parse().map_err(err_map)?), - "isize" => Number::isize(tokens.slice().parse().map_err(err_map)?), + "u8" => Number::u8( + tokens + .slice() + .parse() + .map_err(map_parseint_error(tokens.span()))?, + ), + "u16" => Number::u16( + tokens + .slice() + .parse() + .map_err(map_parseint_error(tokens.span()))?, + ), + "u32" => Number::u32( + tokens + .slice() + .parse() + .map_err(map_parseint_error(tokens.span()))?, + ), + "u64" => Number::u64( + tokens + .slice() + .parse() + .map_err(map_parseint_error(tokens.span()))?, + ), + "usize" => Number::usize( + tokens + .slice() + .parse() + .map_err(map_parseint_error(tokens.span()))?, + ), + "i8" => Number::i8( + tokens + .slice() + .parse() + .map_err(map_parseint_error(tokens.span()))?, + ), + "i16" => Number::i16( + tokens + .slice() + .parse() + .map_err(map_parseint_error(tokens.span()))?, + ), + "i32" => Number::i32( + tokens + .slice() + .parse() + .map_err(map_parseint_error(tokens.span()))?, + ), + "isize" => Number::isize( + tokens + .slice() + .parse() + .map_err(map_parseint_error(tokens.span()))?, + ), "f32" => Number::f32(tokens.slice().parse().unwrap()), "f64" => Number::f64(tokens.slice().parse().unwrap()), _ => unreachable!(), diff --git a/src/builtin_parser/runner.rs b/src/builtin_parser/runner.rs index 78a15ad..ce59187 100644 --- a/src/builtin_parser/runner.rs +++ b/src/builtin_parser/runner.rs @@ -643,7 +643,21 @@ fn eval_path( } } Value::Tuple(tuple) | Value::StructTuple { tuple, .. } => { - todo_error!("eval_path only takes in a ident so this doesn't really work") + if let Access::TupleIndex(right) = right.value { + let weak = match tuple.get(right) { + Some(Spanned { value: rc, span: _ }) => Ok(rc.borrow()), + None => todo_error!(), + }?; + + Ok(left.span.wrap(Path::Variable(weak))) + } else { + Err(RunError::IncorrectAccessOperation { + span: right.span, + expected_access: &["a tuple access"], + expected_type: "a tuple", + got: right.value, + }) + } } value => todo_error!("{value:?}"), }, @@ -656,7 +670,7 @@ fn eval_path( } else { Err(RunError::IncorrectAccessOperation { span: right.span, - expected_access: &["a field access"], + expected_access: &["a field"], expected_type: "a resource", got: right.value, }) From 9f7ddcfb5b9dec2dc6a7651487388d5332971271 Mon Sep 17 00:00:00 2001 From: Doonv <58695417+doonv@users.noreply.github.com> Date: Fri, 26 Jan 2024 23:25:26 +0200 Subject: [PATCH 3/6] Re-add member expressions --- Cargo.lock | 21 ++ Cargo.toml | 1 + src/builtin_parser.rs | 3 - src/builtin_parser/parser.rs | 312 +++++++++++++++++------------ src/builtin_parser/runner.rs | 79 +++++--- src/builtin_parser/runner/error.rs | 22 +- 6 files changed, 273 insertions(+), 165 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ddbf39b..f2ebba6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -536,6 +536,7 @@ dependencies = [ "bevy_egui", "chrono", "console_error_panic_hook", + "const_format", "logos", "tracing-log 0.2.0", "tracing-subscriber", @@ -1395,6 +1396,26 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "const_format" +version = "0.2.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a214c7af3d04997541b18d432afaff4c455e79e2029079647e72fc2bd27673" +dependencies = [ + "const_format_proc_macros", +] + +[[package]] +name = "const_format_proc_macros" +version = "0.2.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7f6ff08fd20f4f299298a28e2dfa8a8ba1036e6cd2460ac1de7b425d76f2500" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + [[package]] name = "const_panic" version = "0.2.8" diff --git a/Cargo.toml b/Cargo.toml index 909f08b..ceb4bac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,7 @@ web-time = "0.2.4" # builtin-parser features logos = { version = "0.13.0", optional = true } +const_format = "0.2.32" [dev-dependencies] bevy = "0.12.1" diff --git a/src/builtin_parser.rs b/src/builtin_parser.rs index 238aabb..282c6af 100644 --- a/src/builtin_parser.rs +++ b/src/builtin_parser.rs @@ -71,9 +71,6 @@ pub struct BuiltinCommandParser; impl CommandParser for BuiltinCommandParser { fn parse(&self, command: &str, world: &mut World) { let mut tokens = TokenStream::new(command); - for a in TokenStream::new(command) { - dbg!(a); - } let environment = world.remove_non_send_resource::().unwrap(); let ast = parse(&mut tokens, &environment); diff --git a/src/builtin_parser/parser.rs b/src/builtin_parser/parser.rs index 72fcf83..1ea0981 100644 --- a/src/builtin_parser/parser.rs +++ b/src/builtin_parser/parser.rs @@ -95,17 +95,59 @@ pub enum Access { // /// An index-based access on a list. // ListIndex(usize), } +pub enum AccessKind { + Field, + TupleIndex, +} impl Access { + pub const fn kind(&self) -> AccessKind { + match self { + Access::Field(_) => AccessKind::Field, + Access::TupleIndex(_) => AccessKind::TupleIndex, + } + } /// Returns the kind of [`Access`] as a [string slice](str) with an `a` or `an` prepended to it. /// Used for more natural sounding error messages. - pub fn natural_kind(&self) -> &'static str { + pub const fn natural_kind(&self) -> &'static str { + self.kind().natural() + } +} + +impl AccessKind { + /// Returns the kind of [`Access`] as a [string slice](str) with an `a` or `an` prepended to it. + /// Used for more natural sounding error messages. + pub const fn natural(&self) -> &'static str { match self { - Access::Field(_) => "a field", - Access::TupleIndex(_) => "a tuple", + AccessKind::Field => "a field", + AccessKind::TupleIndex => "a tuple", } } } +/// Get the access if its of a certain type, if not, return a [`RunError`](super::runner::error::RunError). +/// +/// For examples, take a look at existing uses in the code. +macro_rules! access_unwrap { + ($expected:literal, $($variant:ident($variant_inner:ident))|+ = $val:expr => $block:block) => {{ + let val = $val; + if let $(Access::$variant($variant_inner))|+ = val.value $block else { + use $crate::builtin_parser::parser::AccessKind; + use $crate::builtin_parser::runner::error::RunError; + + // We have to put this in a `const` first to avoid a + // `temporary value dropped while borrowed` error. + const EXPECTED_ACCESS: &[&str] = &[$(AccessKind::$variant.natural()),+]; + Err(RunError::IncorrectAccessOperation { + span: val.span, + expected_access: EXPECTED_ACCESS, + expected_type: $expected, + got: val.value, + })? + } + }}; +} +pub(crate) use access_unwrap; + impl Expression { pub fn kind(&self) -> &'static str { match self { @@ -296,159 +338,172 @@ fn parse_primary( tokens: &mut TokenStream, environment: &Environment, ) -> Result, ParseError> { - let mut expr = match tokens.next() { - Some(Ok(Token::LeftParen)) => { - let start = tokens.span().start; - if let Some(Ok(Token::RightParen)) = tokens.peek() { - tokens.next(); - Ok(Spanned { - span: start..tokens.span().end, - value: Expression::None, - }) - } else { - let expr = parse_expression(tokens, environment)?; - if let Some(Ok(Token::Comma)) = tokens.peek() { - let mut tuple = vec![expr]; - - while let Some(Ok(Token::Comma)) = tokens.peek() { - tokens.next(); - let expr = parse_expression(tokens, environment)?; - - tuple.push(expr); - } - - expect!(tokens, Token::RightParen); - + /// Parses a primary without member expressions + fn parse_subprimary( + tokens: &mut TokenStream, + environment: &Environment, + ) -> Result, ParseError> { + match tokens.next() { + Some(Ok(Token::LeftParen)) => { + let start = tokens.span().start; + if let Some(Ok(Token::RightParen)) = tokens.peek() { + tokens.next(); Ok(Spanned { span: start..tokens.span().end, - value: Expression::Tuple(tuple), + value: Expression::None, }) } else { - expect!(tokens, Token::RightParen); - - Ok(expr) - } - } - } - Some(Ok(Token::Identifer)) => { - let start = tokens.span().start; - let name = tokens.slice().to_string(); - - match tokens.peek() { - Some(Ok(Token::LeftParen)) => { - tokens.next(); - let expr = parse_expression(tokens, environment)?; + if let Some(Ok(Token::Comma)) = tokens.peek() { + let mut tuple = vec![expr]; - let mut tuple = vec![expr]; + while let Some(Ok(Token::Comma)) = tokens.peek() { + tokens.next(); + let expr = parse_expression(tokens, environment)?; - while let Some(Ok(Token::Comma)) = tokens.peek() { - tokens.next(); - let expr = parse_expression(tokens, environment)?; + tuple.push(expr); + } - tuple.push(expr); - } + expect!(tokens, Token::RightParen); - expect!(tokens, Token::RightParen); + Ok(Spanned { + span: start..tokens.span().end, + value: Expression::Tuple(tuple), + }) + } else { + expect!(tokens, Token::RightParen); - Ok(Spanned { - span: start..tokens.span().end, - value: Expression::StructTuple { name, tuple }, - }) + Ok(expr) + } } - Some(Ok(Token::LeftBracket)) => { - tokens.next(); + } + Some(Ok(Token::Identifer)) => { + let start = tokens.span().start; + let name = tokens.slice().to_string(); - let map = parse_object(tokens, environment)?; + match tokens.peek() { + Some(Ok(Token::LeftParen)) => { + tokens.next(); - Ok(Spanned { - span: tokens.span(), - value: Expression::StructObject { name, map }, - }) - } - _ => { - if let Some(Function { argument_count, .. }) = environment.get_function(&name) { - dbg!(argument_count); + let expr = parse_expression(tokens, environment)?; - let mut arguments = Vec::new(); - for _ in 0..(*argument_count) { + let mut tuple = vec![expr]; + + while let Some(Ok(Token::Comma)) = tokens.peek() { + tokens.next(); let expr = parse_expression(tokens, environment)?; - arguments.push(expr); + + tuple.push(expr); } + + expect!(tokens, Token::RightParen); + Ok(Spanned { span: start..tokens.span().end, - value: Expression::Function { name, arguments }, + value: Expression::StructTuple { name, tuple }, }) - } else { - Ok(tokens.wrap_span(Expression::Variable(name))) } - } - } - } - Some(Ok(Token::LeftBracket)) => { - let map = parse_object(tokens, environment)?; - - Ok(Spanned { - span: tokens.span(), - value: Expression::Object(map), - }) - } - Some(Ok(Token::String)) => { - let slice = tokens.slice(); - let string = slice[1..slice.len() - 1].to_string(); - Ok(tokens.wrap_span(Expression::String(string))) - } - Some(Ok(Token::Minus)) => { - let expr = parse_primary(tokens, environment)?; - Ok(tokens.wrap_span(Expression::UnaryOp(Box::new(expr)))) - } - Some(Ok(Token::Ampersand)) => { - let expr = parse_primary(tokens, environment)?; - Ok(tokens.wrap_span(Expression::Borrow(Box::new(expr)))) - } - Some(Ok(Token::Asterisk)) => { - let expr = parse_primary(tokens, environment)?; - Ok(tokens.wrap_span(Expression::Dereference(Box::new(expr)))) - } - Some(Ok(Token::IntegerNumber)) => parse_number(tokens).map(|s| s.map(Expression::Number)), - Some(Ok(Token::FloatNumber)) => { - if let Some(Ok(Token::NumberType)) = tokens.peek() { - let number: Number = match tokens.peek_slice() { - "u8" => Err(ParseError::InvalidSuffixForDecimal(tokens.span()))?, - "u16" => Err(ParseError::InvalidSuffixForDecimal(tokens.span()))?, - "u32" => Err(ParseError::InvalidSuffixForDecimal(tokens.span()))?, - "u64" => Err(ParseError::InvalidSuffixForDecimal(tokens.span()))?, - "i8" => Err(ParseError::InvalidSuffixForDecimal(tokens.span()))?, - "i16" => Err(ParseError::InvalidSuffixForDecimal(tokens.span()))?, - "i32" => Err(ParseError::InvalidSuffixForDecimal(tokens.span()))?, - "i64" => Err(ParseError::InvalidSuffixForDecimal(tokens.span()))?, - "f32" => Number::f32(tokens.slice().parse().unwrap()), - "f64" => Number::f64(tokens.slice().parse().unwrap()), - _ => unreachable!(), - }; - let start_span = tokens.span().end; + Some(Ok(Token::LeftBracket)) => { + tokens.next(); - tokens.next(); + let map = parse_object(tokens, environment)?; - Ok(Spanned { - span: start_span..tokens.span().end, - value: Expression::Number(number), - }) - } else { - let number = Number::Float(tokens.slice().parse().unwrap()); + Ok(Spanned { + span: tokens.span(), + value: Expression::StructObject { name, map }, + }) + } + _ => { + if let Some(Function { argument_count, .. }) = + environment.get_function(&name) + { + dbg!(argument_count); + + let mut arguments = Vec::new(); + for _ in 0..(*argument_count) { + let expr = parse_expression(tokens, environment)?; + arguments.push(expr); + } + Ok(Spanned { + span: start..tokens.span().end, + value: Expression::Function { name, arguments }, + }) + } else { + Ok(tokens.wrap_span(Expression::Variable(name))) + } + } + } + } + Some(Ok(Token::LeftBracket)) => { + let map = parse_object(tokens, environment)?; Ok(Spanned { span: tokens.span(), - value: Expression::Number(number), + value: Expression::Object(map), }) } + Some(Ok(Token::String)) => { + let slice = tokens.slice(); + let string = slice[1..slice.len() - 1].to_string(); + Ok(tokens.wrap_span(Expression::String(string))) + } + Some(Ok(Token::Minus)) => { + let expr = parse_primary(tokens, environment)?; + Ok(tokens.wrap_span(Expression::UnaryOp(Box::new(expr)))) + } + Some(Ok(Token::Ampersand)) => { + let expr = parse_subprimary(tokens, environment)?; + dbg!(&expr); + Ok(tokens.wrap_span(Expression::Borrow(Box::new(expr)))) + } + Some(Ok(Token::Asterisk)) => { + let expr = parse_primary(tokens, environment)?; + Ok(tokens.wrap_span(Expression::Dereference(Box::new(expr)))) + } + Some(Ok(Token::IntegerNumber)) => { + parse_number(tokens).map(|s| s.map(Expression::Number)) + } + Some(Ok(Token::FloatNumber)) => { + if let Some(Ok(Token::NumberType)) = tokens.peek() { + let number: Number = match tokens.peek_slice() { + "u8" => Err(ParseError::InvalidSuffixForDecimal(tokens.span()))?, + "u16" => Err(ParseError::InvalidSuffixForDecimal(tokens.span()))?, + "u32" => Err(ParseError::InvalidSuffixForDecimal(tokens.span()))?, + "u64" => Err(ParseError::InvalidSuffixForDecimal(tokens.span()))?, + "i8" => Err(ParseError::InvalidSuffixForDecimal(tokens.span()))?, + "i16" => Err(ParseError::InvalidSuffixForDecimal(tokens.span()))?, + "i32" => Err(ParseError::InvalidSuffixForDecimal(tokens.span()))?, + "i64" => Err(ParseError::InvalidSuffixForDecimal(tokens.span()))?, + "f32" => Number::f32(tokens.slice().parse().unwrap()), + "f64" => Number::f64(tokens.slice().parse().unwrap()), + _ => unreachable!(), + }; + let start_span = tokens.span().end; + + tokens.next(); + + Ok(Spanned { + span: start_span..tokens.span().end, + value: Expression::Number(number), + }) + } else { + let number = Number::Float(tokens.slice().parse().unwrap()); + + Ok(Spanned { + span: tokens.span(), + value: Expression::Number(number), + }) + } + } + Some(Ok(Token::True)) => Ok(tokens.wrap_span(Expression::Boolean(true))), + Some(Ok(Token::False)) => Ok(tokens.wrap_span(Expression::Boolean(false))), + Some(Ok(token)) => Err(ParseError::UnexpectedToken(tokens.wrap_span(token))), + Some(Err(FailedToLexCharacter)) => Err(ParseError::FailedToLexCharacter(tokens.span())), + None => Err(ParseError::ExpectedMoreTokens(tokens.span())), } - Some(Ok(Token::True)) => Ok(tokens.wrap_span(Expression::Boolean(true))), - Some(Ok(Token::False)) => Ok(tokens.wrap_span(Expression::Boolean(false))), - Some(Ok(token)) => Err(ParseError::UnexpectedToken(tokens.wrap_span(token))), - Some(Err(FailedToLexCharacter)) => Err(ParseError::FailedToLexCharacter(tokens.span())), - None => Err(ParseError::ExpectedMoreTokens(tokens.span())), - }?; + } + + let mut expr = parse_subprimary(tokens, environment)?; // If theres a dot after the expression, do a member expression: while let Some(Ok(Token::Dot)) = tokens.peek() { tokens.next(); // Skip the dot @@ -475,7 +530,6 @@ fn parse_primary( right: tokens.wrap_span(Access::TupleIndex(right)), }, }; - } _ => todo!(), } diff --git a/src/builtin_parser/runner.rs b/src/builtin_parser/runner.rs index ce59187..1299ca3 100644 --- a/src/builtin_parser/runner.rs +++ b/src/builtin_parser/runner.rs @@ -8,6 +8,7 @@ use bevy::reflect::{ DynamicEnum, DynamicTuple, ReflectMut, TypeInfo, TypeRegistration, VariantInfo, }; +use crate::builtin_parser::parser::access_unwrap; use crate::command::CommandHints; use crate::ui::COMMAND_RESULT_NAME; @@ -446,7 +447,15 @@ fn eval_expression( loop_count, block, } => todo_error!("for loop {index_name}, {loop_count}, {block:#?}"), - Expression::Member { left, right } => todo!(), + Expression::Member { left, right } => eval_member_expression( + *left, + right, + EvalParams { + world, + environment, + registrations, + }, + ), Expression::UnaryOp(sub_expr) => { let span = sub_expr.span.clone(); let value = eval_expression( @@ -524,16 +533,14 @@ fn eval_expression( fn eval_member_expression( left: Spanned, - right: String, - params: EvalParams, -) -> Result { - let left_span = left.span.clone(); - let EvalParams { + right: Spanned, + EvalParams { world, environment, registrations, - } = params; - // TODO: Add ability to borrow from a struct. + }: EvalParams, +) -> Result { + let span = left.span.start..right.span.end; let left = eval_expression( left, EvalParams { @@ -545,19 +552,23 @@ fn eval_member_expression( match left { Value::Object(mut map) | Value::StructObject { mut map, .. } => { - let value = map - .remove(&right) - .ok_or(RunError::FieldNotFoundInStruct(left_span))?; + access_unwrap!("an object", Field(field) = right => { + let value = map + .remove(&field) + .ok_or(RunError::FieldNotFoundInStruct(span.wrap(field)))?; - Ok(value.into_inner()) + Ok(value.into_inner()) + }) } Value::Resource(mut resource) => { - resource.path.push('.'); - resource.path += &right; + access_unwrap!("a resource", Field(field) = right => { + resource.path.push('.'); + resource.path += &field; - Ok(Value::Resource(resource)) + Ok(Value::Resource(resource)) + }) } - _ => Err(RunError::CannotIndexValue(left_span.wrap(left))), + _ => Err(RunError::CannotIndexValue(span.wrap(left))), } } @@ -626,13 +637,16 @@ fn eval_path( } } Value::Object(object) | Value::StructObject { map: object, .. } => { - if let Access::Field(right) = right.value { - let weak = match object.get(&right) { - Some(rc) => Ok(rc.borrow()), - None => todo_error!(), - }?; + if let Access::Field(field) = right.value { + let span = left.span.join(right.span); + let weak = match object.get(&field) { + Some(rc) => rc.borrow(), + None => { + return Err(RunError::FieldNotFoundInStruct(span.wrap(field))) + } + }; - Ok(left.span.wrap(Path::Variable(weak))) + Ok(span.wrap(Path::Variable(weak))) } else { Err(RunError::IncorrectAccessOperation { span: right.span, @@ -643,13 +657,20 @@ fn eval_path( } } Value::Tuple(tuple) | Value::StructTuple { tuple, .. } => { - if let Access::TupleIndex(right) = right.value { - let weak = match tuple.get(right) { - Some(Spanned { value: rc, span: _ }) => Ok(rc.borrow()), - None => todo_error!(), - }?; + if let Access::TupleIndex(index) = right.value { + let span = left.span.join(right.span); + let weak = match tuple.get(index) { + Some(Spanned { value: rc, span: _ }) => rc.borrow(), + None => { + return Err(RunError::FieldNotFoundInTuple { + span, + field_index: index, + tuple_size: tuple.len(), + }) + } + }; - Ok(left.span.wrap(Path::Variable(weak))) + Ok(span.wrap(Path::Variable(weak))) } else { Err(RunError::IncorrectAccessOperation { span: right.span, @@ -707,7 +728,7 @@ fn eval_path( Path::Resource(_) => todo_error!(), } } - expr => todo_error!("{expr:#?}"), + expr => todo_error!("can't eval path of this expr: {expr:#?}"), } } diff --git a/src/builtin_parser/runner/error.rs b/src/builtin_parser/runner/error.rs index 3dc815c..f317d3a 100644 --- a/src/builtin_parser/runner/error.rs +++ b/src/builtin_parser/runner/error.rs @@ -28,7 +28,6 @@ pub enum RunError { VariableNotFound(Spanned), ExpectedNumberAfterUnaryOperator(Spanned), CannotIndexValue(Spanned), - FieldNotFoundInStruct(Span), ReferenceToMovedData(Span), VariableMoved(Spanned), CannotDereferenceValue(Spanned<&'static str>), @@ -70,6 +69,12 @@ pub enum RunError { expected_type: &'static str, got: Access, }, + FieldNotFoundInStruct(Spanned), + FieldNotFoundInTuple { + span: Span, + field_index: usize, + tuple_size: usize, + }, } impl RunError { @@ -82,7 +87,7 @@ impl RunError { VariableNotFound(Spanned { span, .. }) => vec![span.clone()], ExpectedNumberAfterUnaryOperator(Spanned { span, .. }) => vec![span.clone()], CannotIndexValue(Spanned { span, .. }) => vec![span.clone()], - FieldNotFoundInStruct(span) => vec![span.clone()], + FieldNotFoundInStruct(Spanned { span, value: _ }) => vec![span.clone()], CannotDereferenceValue(Spanned { span, .. }) => vec![span.clone()], ReferenceToMovedData(span) => vec![span.clone()], VariableMoved(Spanned { span, .. }) => vec![span.clone()], @@ -100,6 +105,7 @@ impl RunError { CannotReflectResource(span) => vec![span.clone()], InvalidOperation { span, .. } => vec![span.clone()], IncorrectAccessOperation { span, .. } => vec![span.clone()], + FieldNotFoundInTuple { span, .. } => vec![span.clone()], } } /// Returns all the hints for this error. @@ -126,14 +132,13 @@ impl RunError { CannotIndexValue(Spanned { span: _, value }) => { format!("Cannot index {} with a member expression.", value.kind()).into() } - FieldNotFoundInStruct(_) => todo!(), ReferenceToMovedData(_) => todo!(), VariableMoved(Spanned { value, .. }) => format!("Variable `{value}` was moved.").into(), CannotDereferenceValue(Spanned { value: kind, .. }) => { format!("Cannot dereference {kind}.").into() } CannotBorrowValue(Spanned { value: kind, .. }) => { - format!("Cannot borrow {kind}.").into() + format!("Cannot borrow {kind}. Only variables can be borrowed.").into() } IncompatibleReflectTypes { expected, actual, .. @@ -200,6 +205,15 @@ impl RunError { got.natural_kind() ) .into(), + FieldNotFoundInStruct(Spanned { span: _, value }) => { + format!("Field {value} not found in struct").into() + } + FieldNotFoundInTuple { + field_index, + tuple_size, + span: _, + } => format!("Field {field_index} is out of bounds for tuple of size {tuple_size}") + .into(), } } } From 67694390c874b702f46bc20a81806b4e610a923e Mon Sep 17 00:00:00 2001 From: Doonv <58695417+doonv@users.noreply.github.com> Date: Sat, 27 Jan 2024 14:25:30 +0200 Subject: [PATCH 4/6] Tuple indexing member expressions and reference member expressions --- src/builtin_parser/parser.rs | 2 +- src/builtin_parser/runner.rs | 88 +++++++++++++++++++++++------------- 2 files changed, 58 insertions(+), 32 deletions(-) diff --git a/src/builtin_parser/parser.rs b/src/builtin_parser/parser.rs index 1ea0981..4a328a4 100644 --- a/src/builtin_parser/parser.rs +++ b/src/builtin_parser/parser.rs @@ -125,7 +125,7 @@ impl AccessKind { } /// Get the access if its of a certain type, if not, return a [`RunError`](super::runner::error::RunError). -/// +/// /// For examples, take a look at existing uses in the code. macro_rules! access_unwrap { ($expected:literal, $($variant:ident($variant_inner:ident))|+ = $val:expr => $block:block) => {{ diff --git a/src/builtin_parser/runner.rs b/src/builtin_parser/runner.rs index 1299ca3..b2fa3f3 100644 --- a/src/builtin_parser/runner.rs +++ b/src/builtin_parser/runner.rs @@ -540,6 +540,7 @@ fn eval_member_expression( registrations, }: EvalParams, ) -> Result { + let left_span = left.span.clone(); let span = left.span.start..right.span.end; let left = eval_expression( left, @@ -551,6 +552,35 @@ fn eval_member_expression( )?; match left { + Value::Reference(reference) => { + let Some(strong) = reference.upgrade() else { + return Err(RunError::ReferenceToMovedData(left_span)); + }; + let reference = strong.borrow(); + match &&*reference { + Value::Object(map) | Value::StructObject { map, .. } => { + access_unwrap!("an object reference", Field(field) = right => { + let value = map.get(&field).ok_or(RunError::FieldNotFoundInStruct(span.wrap(field)))?; + + Ok(Value::Reference(value.borrow())) + }) + } + Value::Tuple(tuple) | Value::StructTuple { tuple, .. } => { + access_unwrap!("a tuple reference", TupleIndex(index) = right => { + let Spanned { span: _, value } = + tuple.get(index).ok_or(RunError::FieldNotFoundInTuple { + span, + field_index: index, + tuple_size: tuple.len(), + })?; + + Ok(Value::Reference(value.borrow())) + }) + } + Value::Resource(resource) => todo_error!("todo resource reference"), + var => Err(RunError::CannotIndexValue(left_span.wrap((*var).clone()))), + } + } Value::Object(mut map) | Value::StructObject { mut map, .. } => { access_unwrap!("an object", Field(field) = right => { let value = map @@ -560,6 +590,23 @@ fn eval_member_expression( Ok(value.into_inner()) }) } + Value::Tuple(tuple) | Value::StructTuple { tuple, .. } => { + access_unwrap!("a tuple reference", TupleIndex(field_index) = right => { + let tuple_size = tuple.len(); + let Spanned { span: _, value } = + tuple + .into_vec() + .into_iter() + .nth(field_index) + .ok_or(RunError::FieldNotFoundInTuple { + span, + field_index, + tuple_size, + })?; + + Ok(value.into_inner()) + }) + } Value::Resource(mut resource) => { access_unwrap!("a resource", Field(field) = right => { resource.path.push('.'); @@ -568,7 +615,7 @@ fn eval_member_expression( Ok(Value::Resource(resource)) }) } - _ => Err(RunError::CannotIndexValue(span.wrap(left))), + _ => Err(RunError::CannotIndexValue(left_span.wrap(left))), } } @@ -620,25 +667,18 @@ fn eval_path( match left.value { Path::Variable(variable) => match &*variable.upgrade().unwrap().borrow() { Value::Resource(resource) => { - if let Access::Field(right) = right.value { + access_unwrap!("a resource", Field(field) = right => { let mut resource = resource.clone(); resource.path.push('.'); - resource.path += &right; + resource.path += &field; Ok(left.span.wrap(Path::Resource(resource))) - } else { - Err(RunError::IncorrectAccessOperation { - span: right.span, - expected_access: &["a field access"], - expected_type: "a resource", - got: right.value, - }) - } + }) } Value::Object(object) | Value::StructObject { map: object, .. } => { - if let Access::Field(field) = right.value { - let span = left.span.join(right.span); + let span = left.span.start..right.span.end; + access_unwrap!("an object", Field(field) = right => { let weak = match object.get(&field) { Some(rc) => rc.borrow(), None => { @@ -647,18 +687,11 @@ fn eval_path( }; Ok(span.wrap(Path::Variable(weak))) - } else { - Err(RunError::IncorrectAccessOperation { - span: right.span, - expected_access: &["a field access"], - expected_type: "an object", - got: right.value, - }) - } + }) } Value::Tuple(tuple) | Value::StructTuple { tuple, .. } => { - if let Access::TupleIndex(index) = right.value { - let span = left.span.join(right.span); + let span = left.span.start..right.span.end; + access_unwrap!("a tupple", TupleIndex(index) = right => { let weak = match tuple.get(index) { Some(Spanned { value: rc, span: _ }) => rc.borrow(), None => { @@ -671,14 +704,7 @@ fn eval_path( }; Ok(span.wrap(Path::Variable(weak))) - } else { - Err(RunError::IncorrectAccessOperation { - span: right.span, - expected_access: &["a tuple access"], - expected_type: "a tuple", - got: right.value, - }) - } + }) } value => todo_error!("{value:?}"), }, From aa0f3f71681d832f5986bd6df10bf61610655203 Mon Sep 17 00:00:00 2001 From: Doonv <58695417+doonv@users.noreply.github.com> Date: Sat, 27 Jan 2024 14:42:48 +0200 Subject: [PATCH 5/6] Resource member expressions --- src/builtin_parser/runner.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/builtin_parser/runner.rs b/src/builtin_parser/runner.rs index b2fa3f3..f989017 100644 --- a/src/builtin_parser/runner.rs +++ b/src/builtin_parser/runner.rs @@ -577,7 +577,16 @@ fn eval_member_expression( Ok(Value::Reference(value.borrow())) }) } - Value::Resource(resource) => todo_error!("todo resource reference"), + Value::Resource(resource) => { + access_unwrap!("a resource", Field(field) = right => { + let mut resource = resource.clone(); + + resource.path.push('.'); + resource.path += &field; + + Ok(Value::Resource(resource)) + }) + } var => Err(RunError::CannotIndexValue(left_span.wrap((*var).clone()))), } } From 8e70a8d7491e919b69c51070595c925c6534d63f Mon Sep 17 00:00:00 2001 From: Doonv <58695417+doonv@users.noreply.github.com> Date: Sat, 27 Jan 2024 15:11:22 +0200 Subject: [PATCH 6/6] Move member & path stuff into seperate module --- src/builtin_parser/runner.rs | 257 ++---------------------- src/builtin_parser/runner/member.rs | 267 +++++++++++++++++++++++++ src/builtin_parser/runner/unique_rc.rs | 2 +- 3 files changed, 280 insertions(+), 246 deletions(-) create mode 100644 src/builtin_parser/runner/member.rs diff --git a/src/builtin_parser/runner.rs b/src/builtin_parser/runner.rs index f989017..9676c03 100644 --- a/src/builtin_parser/runner.rs +++ b/src/builtin_parser/runner.rs @@ -8,23 +8,24 @@ use bevy::reflect::{ DynamicEnum, DynamicTuple, ReflectMut, TypeInfo, TypeRegistration, VariantInfo, }; -use crate::builtin_parser::parser::access_unwrap; use crate::command::CommandHints; use crate::ui::COMMAND_RESULT_NAME; use self::error::RunError; +use self::member::{eval_member_expression, eval_path, Path}; use self::reflection::{object_to_dynamic_struct, CreateRegistration, IntoResource}; -use self::unique_rc::{UniqueRc, WeakRef}; +use self::unique_rc::UniqueRc; -use super::parser::{Access, Ast, Expression, Operator}; +use super::parser::{Ast, Expression, Operator}; use super::{Number, SpanExtension, Spanned}; -pub mod environment; -pub mod error; -pub mod reflection; -pub mod stdlib; -pub mod unique_rc; -pub mod value; +pub(super) mod environment; +pub(super) mod error; +pub(super) mod member; +pub(super) mod reflection; +pub(super) mod stdlib; +pub(super) mod unique_rc; +pub(super) mod value; pub use value::Value; @@ -43,6 +44,8 @@ macro_rules! todo_error { })? }; } +// This makes `todo_error` accessible to the runners submodules +use todo_error; /// Container for every value needed by evaluation functions. pub struct EvalParams<'world, 'env, 'reg> { @@ -531,242 +534,6 @@ fn eval_expression( } } -fn eval_member_expression( - left: Spanned, - right: Spanned, - EvalParams { - world, - environment, - registrations, - }: EvalParams, -) -> Result { - let left_span = left.span.clone(); - let span = left.span.start..right.span.end; - let left = eval_expression( - left, - EvalParams { - world, - environment, - registrations, - }, - )?; - - match left { - Value::Reference(reference) => { - let Some(strong) = reference.upgrade() else { - return Err(RunError::ReferenceToMovedData(left_span)); - }; - let reference = strong.borrow(); - match &&*reference { - Value::Object(map) | Value::StructObject { map, .. } => { - access_unwrap!("an object reference", Field(field) = right => { - let value = map.get(&field).ok_or(RunError::FieldNotFoundInStruct(span.wrap(field)))?; - - Ok(Value::Reference(value.borrow())) - }) - } - Value::Tuple(tuple) | Value::StructTuple { tuple, .. } => { - access_unwrap!("a tuple reference", TupleIndex(index) = right => { - let Spanned { span: _, value } = - tuple.get(index).ok_or(RunError::FieldNotFoundInTuple { - span, - field_index: index, - tuple_size: tuple.len(), - })?; - - Ok(Value::Reference(value.borrow())) - }) - } - Value::Resource(resource) => { - access_unwrap!("a resource", Field(field) = right => { - let mut resource = resource.clone(); - - resource.path.push('.'); - resource.path += &field; - - Ok(Value::Resource(resource)) - }) - } - var => Err(RunError::CannotIndexValue(left_span.wrap((*var).clone()))), - } - } - Value::Object(mut map) | Value::StructObject { mut map, .. } => { - access_unwrap!("an object", Field(field) = right => { - let value = map - .remove(&field) - .ok_or(RunError::FieldNotFoundInStruct(span.wrap(field)))?; - - Ok(value.into_inner()) - }) - } - Value::Tuple(tuple) | Value::StructTuple { tuple, .. } => { - access_unwrap!("a tuple reference", TupleIndex(field_index) = right => { - let tuple_size = tuple.len(); - let Spanned { span: _, value } = - tuple - .into_vec() - .into_iter() - .nth(field_index) - .ok_or(RunError::FieldNotFoundInTuple { - span, - field_index, - tuple_size, - })?; - - Ok(value.into_inner()) - }) - } - Value::Resource(mut resource) => { - access_unwrap!("a resource", Field(field) = right => { - resource.path.push('.'); - resource.path += &field; - - Ok(Value::Resource(resource)) - }) - } - _ => Err(RunError::CannotIndexValue(left_span.wrap(left))), - } -} - -enum Path { - Variable(WeakRef), - NewVariable(String), - Resource(IntoResource), -} - -fn eval_path( - expr: Spanned, - EvalParams { - world, - environment, - registrations, - }: EvalParams, -) -> Result, RunError> { - match expr.value { - Expression::Variable(variable) => { - if let Some(registration) = registrations - .iter() - .find(|v| v.type_info().type_path_table().short_path() == variable) - { - Ok(Spanned { - span: expr.span, - value: Path::Resource(IntoResource::new(registration.type_id())), - }) - } else if let Ok(variable) = environment.get(&variable, expr.span.clone()) { - Ok(Spanned { - span: expr.span, - value: Path::Variable(variable.borrow()), - }) - } else { - Ok(Spanned { - span: expr.span, - value: Path::NewVariable(variable), - }) - } - } - Expression::Member { left, right } => { - let left = eval_path( - *left, - EvalParams { - world, - environment, - registrations, - }, - )?; - match left.value { - Path::Variable(variable) => match &*variable.upgrade().unwrap().borrow() { - Value::Resource(resource) => { - access_unwrap!("a resource", Field(field) = right => { - let mut resource = resource.clone(); - - resource.path.push('.'); - resource.path += &field; - - Ok(left.span.wrap(Path::Resource(resource))) - }) - } - Value::Object(object) | Value::StructObject { map: object, .. } => { - let span = left.span.start..right.span.end; - access_unwrap!("an object", Field(field) = right => { - let weak = match object.get(&field) { - Some(rc) => rc.borrow(), - None => { - return Err(RunError::FieldNotFoundInStruct(span.wrap(field))) - } - }; - - Ok(span.wrap(Path::Variable(weak))) - }) - } - Value::Tuple(tuple) | Value::StructTuple { tuple, .. } => { - let span = left.span.start..right.span.end; - access_unwrap!("a tupple", TupleIndex(index) = right => { - let weak = match tuple.get(index) { - Some(Spanned { value: rc, span: _ }) => rc.borrow(), - None => { - return Err(RunError::FieldNotFoundInTuple { - span, - field_index: index, - tuple_size: tuple.len(), - }) - } - }; - - Ok(span.wrap(Path::Variable(weak))) - }) - } - value => todo_error!("{value:?}"), - }, - Path::Resource(mut resource) => { - if let Access::Field(right) = right.value { - resource.path.push('.'); - resource.path += &right; - - Ok(left.span.wrap(Path::Resource(resource))) - } else { - Err(RunError::IncorrectAccessOperation { - span: right.span, - expected_access: &["a field"], - expected_type: "a resource", - got: right.value, - }) - } - } - Path::NewVariable(name) => Err(RunError::VariableNotFound(left.span.wrap(name))), - } - } - Expression::Dereference(inner) => { - let path = eval_path( - *inner, - EvalParams { - world, - environment, - registrations, - }, - )?; - match path.value { - Path::Variable(value) => { - let strong = value - .upgrade() - .ok_or(RunError::ReferenceToMovedData(path.span))?; - let borrow = strong.borrow(); - - if let Value::Reference(ref reference) = &*borrow { - Ok(expr.span.wrap(Path::Variable(reference.clone()))) - } else { - Err(RunError::CannotDereferenceValue( - expr.span.wrap(borrow.natural_kind()), - )) - } - } - Path::NewVariable(_) => todo_error!(), - Path::Resource(_) => todo_error!(), - } - } - expr => todo_error!("can't eval path of this expr: {expr:#?}"), - } -} - fn eval_object( map: HashMap>, EvalParams { diff --git a/src/builtin_parser/runner/member.rs b/src/builtin_parser/runner/member.rs new file mode 100644 index 0000000..5dae8a3 --- /dev/null +++ b/src/builtin_parser/runner/member.rs @@ -0,0 +1,267 @@ +//! Evaluation for member expressions and paths + +use crate::builtin_parser::parser::{access_unwrap, Access, Expression}; +use crate::builtin_parser::{RunError, SpanExtension, Spanned, WeakRef}; + +use super::reflection::IntoResource; +use super::{eval_expression, todo_error, EvalParams, Value}; + +/// Evaluate a member expression. +/// +/// A member expression allows indexing of values to access their inner fields. +/// +/// # Examples +/// +/// ```text +/// $ {a: 5}.a +/// > 5 +/// $ x = {key: "hi"} +/// $ &x.key +/// > "hi" +/// $ x +/// > {key: "hi"} +/// $ x.key +/// > "hi" +/// $ x +/// Error: Variable moved +/// ``` +pub fn eval_member_expression( + left: Spanned, + right: Spanned, + EvalParams { + world, + environment, + registrations, + }: EvalParams, +) -> Result { + let left_span = left.span.clone(); + let span = left.span.start..right.span.end; + let left = eval_expression( + left, + EvalParams { + world, + environment, + registrations, + }, + )?; + + match left { + Value::Reference(reference) => { + let Some(strong) = reference.upgrade() else { + return Err(RunError::ReferenceToMovedData(left_span)); + }; + let reference = strong.borrow(); + match &&*reference { + Value::Object(map) | Value::StructObject { map, .. } => { + access_unwrap!("an object reference", Field(field) = right => { + let value = map.get(&field).ok_or(RunError::FieldNotFoundInStruct(span.wrap(field)))?; + + Ok(Value::Reference(value.borrow())) + }) + } + Value::Tuple(tuple) | Value::StructTuple { tuple, .. } => { + access_unwrap!("a tuple reference", TupleIndex(index) = right => { + let Spanned { span: _, value } = + tuple.get(index).ok_or(RunError::FieldNotFoundInTuple { + span, + field_index: index, + tuple_size: tuple.len(), + })?; + + Ok(Value::Reference(value.borrow())) + }) + } + Value::Resource(resource) => { + access_unwrap!("a resource", Field(field) = right => { + let mut resource = resource.clone(); + + resource.path.push('.'); + resource.path += &field; + + Ok(Value::Resource(resource)) + }) + } + var => Err(RunError::CannotIndexValue(left_span.wrap((*var).clone()))), + } + } + Value::Object(mut map) | Value::StructObject { mut map, .. } => { + access_unwrap!("an object", Field(field) = right => { + let value = map + .remove(&field) + .ok_or(RunError::FieldNotFoundInStruct(span.wrap(field)))?; + + Ok(value.into_inner()) + }) + } + Value::Tuple(tuple) | Value::StructTuple { tuple, .. } => { + access_unwrap!("a tuple reference", TupleIndex(field_index) = right => { + let tuple_size = tuple.len(); + let Spanned { span: _, value } = + tuple + .into_vec() + .into_iter() + .nth(field_index) + .ok_or(RunError::FieldNotFoundInTuple { + span, + field_index, + tuple_size, + })?; + + Ok(value.into_inner()) + }) + } + Value::Resource(mut resource) => { + access_unwrap!("a resource", Field(field) = right => { + resource.path.push('.'); + resource.path += &field; + + Ok(Value::Resource(resource)) + }) + } + _ => Err(RunError::CannotIndexValue(left_span.wrap(left))), + } +} + +pub enum Path { + Variable(WeakRef), + NewVariable(String), + Resource(IntoResource), +} + +/// Evaluate a path expression. +/// +/// A path expression, in contrast to a member expression, is for creating new variables or assigning to existing ones. +/// +/// # Examples +/// +/// ```text +/// a -> Path::NewVariable("a") +/// a.b -> if a.b exists, returns Path::Variable(a.b) +/// MyResource.field -> appends "field" to the IntoResource path and returns Resource +/// a.b.c -> if a.b.c, returns Path::Variable(a.b.c) (wow look its recursive) +/// ``` +pub fn eval_path( + expr: Spanned, + EvalParams { + world, + environment, + registrations, + }: EvalParams, +) -> Result, RunError> { + match expr.value { + Expression::Variable(variable) => { + if let Some(registration) = registrations + .iter() + .find(|v| v.type_info().type_path_table().short_path() == variable) + { + Ok(Spanned { + span: expr.span, + value: Path::Resource(IntoResource::new(registration.type_id())), + }) + } else if let Ok(variable) = environment.get(&variable, expr.span.clone()) { + Ok(Spanned { + span: expr.span, + value: Path::Variable(variable.borrow()), + }) + } else { + Ok(Spanned { + span: expr.span, + value: Path::NewVariable(variable), + }) + } + } + Expression::Member { left, right } => { + let left = eval_path( + *left, + EvalParams { + world, + environment, + registrations, + }, + )?; + match left.value { + Path::Variable(variable) => match &*variable.upgrade().unwrap().borrow() { + Value::Resource(resource) => { + access_unwrap!("a resource", Field(field) = right => { + let mut resource = resource.clone(); + + resource.path.push('.'); + resource.path += &field; + + Ok(left.span.wrap(Path::Resource(resource))) + }) + } + Value::Object(object) | Value::StructObject { map: object, .. } => { + let span = left.span.start..right.span.end; + access_unwrap!("an object", Field(field) = right => { + let weak = match object.get(&field) { + Some(rc) => rc.borrow(), + None => { + return Err(RunError::FieldNotFoundInStruct(span.wrap(field))) + } + }; + + Ok(span.wrap(Path::Variable(weak))) + }) + } + Value::Tuple(tuple) | Value::StructTuple { tuple, .. } => { + let span = left.span.start..right.span.end; + access_unwrap!("a tuple", TupleIndex(index) = right => { + let weak = match tuple.get(index) { + Some(Spanned { value: rc, span: _ }) => rc.borrow(), + None => { + return Err(RunError::FieldNotFoundInTuple { + span, + field_index: index, + tuple_size: tuple.len(), + }) + } + }; + + Ok(span.wrap(Path::Variable(weak))) + }) + } + value => todo_error!("{value:?}"), + }, + Path::Resource(mut resource) => { + access_unwrap!("a resource", Field(field) = right => { + resource.path.push('.'); + resource.path += &field; + + Ok(left.span.wrap(Path::Resource(resource))) + }) + } + Path::NewVariable(name) => Err(RunError::VariableNotFound(left.span.wrap(name))), + } + } + Expression::Dereference(inner) => { + let path = eval_path( + *inner, + EvalParams { + world, + environment, + registrations, + }, + )?; + match path.value { + Path::Variable(value) => { + let strong = value + .upgrade() + .ok_or(RunError::ReferenceToMovedData(path.span))?; + let borrow = strong.borrow(); + + if let Value::Reference(ref reference) = &*borrow { + Ok(expr.span.wrap(Path::Variable(reference.clone()))) + } else { + Err(RunError::CannotDereferenceValue( + expr.span.wrap(borrow.natural_kind()), + )) + } + } + Path::NewVariable(_) => todo_error!(), + Path::Resource(_) => todo_error!(), + } + } + expr => todo_error!("can't eval path of this expr: {expr:#?}"), + } +} diff --git a/src/builtin_parser/runner/unique_rc.rs b/src/builtin_parser/runner/unique_rc.rs index 5e26a5c..ab3adc9 100644 --- a/src/builtin_parser/runner/unique_rc.rs +++ b/src/builtin_parser/runner/unique_rc.rs @@ -66,7 +66,7 @@ impl DerefMut for UniqueRc { } } -/// A weak reference to a [`UniqueRc`] may or may not exist. +/// A weak reference to a [`UniqueRc`] that may or may not exist. #[derive(Debug)] pub struct WeakRef { reference: Weak>,