diff --git a/src/engine.rs b/src/engine.rs index d4c326ce..63fb15ea 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -1454,8 +1454,9 @@ impl Engine { Some((result, rhs_expr.position())) }; + // Must be either `var[index] op= val` or `var.prop op= val` match lhs_expr { - // name op= rhs + // name op= rhs (handled above) Expr::Variable(_) => unreachable!(), // idx_lhs[idx_expr] op= rhs #[cfg(not(feature = "no_index"))] @@ -1473,12 +1474,8 @@ impl Engine { )?; Ok(Default::default()) } - // Error assignment to constant - expr if expr.is_constant() => EvalAltResult::ErrorAssignmentToConstant( - expr.get_constant_str(), - expr.position(), - ) - .into(), + // Constant expression (should be caught during parsing) + expr if expr.is_constant() => unreachable!(), // Syntax error expr => EvalAltResult::ErrorAssignmentToUnknownLHS(expr.position()).into(), } diff --git a/src/optimize.rs b/src/optimize.rs index c1b2b0e2..73c5e015 100644 --- a/src/optimize.rs +++ b/src/optimize.rs @@ -585,7 +585,7 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr { => { let ((name, _, _, pos), _, _, args, _) = x.as_mut(); - let arg_values: StaticVec<_> = args.iter().map(Expr::get_constant_value).collect(); + let arg_values: StaticVec<_> = args.iter().map(|e| e.get_constant_value().unwrap()).collect(); let arg_types: StaticVec<_> = arg_values.iter().map(Dynamic::type_id).collect(); // Search for overloaded operators (can override built-in). @@ -618,7 +618,7 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr { let has_script_fn = false; if !has_script_fn { - let mut arg_values: StaticVec<_> = args.iter().map(Expr::get_constant_value).collect(); + let mut arg_values: StaticVec<_> = args.iter().map(|e| e.get_constant_value().unwrap()).collect(); // Save the typename of the first argument if it is `type_of()` // This is to avoid `call_args` being passed into the closure diff --git a/src/parser.rs b/src/parser.rs index f85215d4..a4dd3b1c 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -12,13 +12,17 @@ use crate::syntax::FnCustomSyntaxEval; use crate::token::{is_keyword_function, is_valid_identifier, Position, Token, TokenStream}; use crate::utils::{StaticVec, StraightHasherBuilder}; +#[cfg(not(feature = "no_index"))] +use crate::engine::Array; + +#[cfg(not(feature = "no_object"))] +use crate::engine::{make_getter, make_setter, Map}; + #[cfg(not(feature = "no_function"))] use crate::engine::{FN_ANONYMOUS, KEYWORD_FN_PTR_CURRY}; -#[cfg(not(feature = "no_object"))] -use crate::engine::{make_getter, make_setter}; - use crate::stdlib::{ + any::TypeId, borrow::Cow, boxed::Box, char, @@ -847,14 +851,40 @@ impl Default for Expr { } impl Expr { + /// Get the type of an expression. + /// + /// Returns `None` if the expression's result type is not constant. + pub fn get_type_id(&self) -> Option { + Some(match self { + Self::Expr(x) => return x.get_type_id(), + + Self::IntegerConstant(_) => TypeId::of::(), + #[cfg(not(feature = "no_float"))] + Self::FloatConstant(_) => TypeId::of::(), + Self::CharConstant(_) => TypeId::of::(), + Self::StringConstant(_) => TypeId::of::(), + Self::FnPointer(_) => TypeId::of::(), + Self::True(_) | Self::False(_) | Self::In(_) | Self::And(_) | Self::Or(_) => { + TypeId::of::() + } + Self::Unit(_) => TypeId::of::<()>(), + + #[cfg(not(feature = "no_index"))] + Self::Array(_) => TypeId::of::(), + + #[cfg(not(feature = "no_object"))] + Self::Map(_) => TypeId::of::(), + + _ => return None, + }) + } + /// Get the `Dynamic` value of a constant expression. /// - /// # Panics - /// - /// Panics when the expression is not constant. - pub fn get_constant_value(&self) -> Dynamic { - match self { - Self::Expr(x) => x.get_constant_value(), + /// Returns `None` if the expression is not constant. + pub fn get_constant_value(&self) -> Option { + Some(match self { + Self::Expr(x) => return x.get_constant_value(), Self::IntegerConstant(x) => x.0.into(), #[cfg(not(feature = "no_float"))] @@ -871,45 +901,22 @@ impl Expr { #[cfg(not(feature = "no_index"))] Self::Array(x) if x.0.iter().all(Self::is_constant) => Dynamic(Union::Array(Box::new( - x.0.iter().map(Self::get_constant_value).collect::>(), + x.0.iter() + .map(|v| v.get_constant_value().unwrap()) + .collect(), ))), #[cfg(not(feature = "no_object"))] Self::Map(x) if x.0.iter().all(|(_, v)| v.is_constant()) => { Dynamic(Union::Map(Box::new( x.0.iter() - .map(|((k, _), v)| (k.clone(), v.get_constant_value())) - .collect::>(), + .map(|((k, _), v)| (k.clone(), v.get_constant_value().unwrap())) + .collect(), ))) } - _ => unreachable!("cannot get value of non-constant expression"), - } - } - - /// Get the display value of a constant expression. - /// - /// # Panics - /// - /// Panics when the expression is not constant. - pub fn get_constant_str(&self) -> String { - match self { - Self::Expr(x) => x.get_constant_str(), - - #[cfg(not(feature = "no_float"))] - Self::FloatConstant(x) => x.0.to_string(), - - Self::IntegerConstant(x) => x.0.to_string(), - Self::CharConstant(x) => x.0.to_string(), - Self::StringConstant(_) => "string".to_string(), - Self::True(_) => "true".to_string(), - Self::False(_) => "false".to_string(), - Self::Unit(_) => "()".to_string(), - - Self::Array(x) if x.0.iter().all(Self::is_constant) => "array".to_string(), - - _ => unreachable!("cannot get value of non-constant expression"), - } + _ => return None, + }) } /// Get the `Position` of the expression.