diff --git a/CHANGELOG.md b/CHANGELOG.md index 10d05cbd..2eb7b1e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ Bug fixes * `switch` cases with conditions that evaluate to constant `()` no longer optimize to `false` (should raise a type error during runtime). * Fixes concatenation of BLOB's and strings, where the BLOB's should be interpreted as UTF-8 encoded strings. * Capturing an unknown variable in a closure no longer panics. +* Fixes panic in interpolated strings with constant expressions. New features ------------ diff --git a/src/ast/expr.rs b/src/ast/expr.rs index 4d6a4b69..193324fa 100644 --- a/src/ast/expr.rs +++ b/src/ast/expr.rs @@ -5,12 +5,16 @@ use crate::engine::{KEYWORD_FN_PTR, OP_EXCLUSIVE_RANGE, OP_INCLUSIVE_RANGE}; use crate::func::hashing::ALT_ZERO_HASH; use crate::tokenizer::Token; use crate::types::dynamic::Union; -use crate::{calc_fn_hash, Dynamic, FnPtr, Identifier, ImmutableString, Position, StaticVec, INT}; +use crate::{ + calc_fn_hash, Dynamic, FnPtr, Identifier, ImmutableString, Position, SmartString, StaticVec, + INT, +}; #[cfg(feature = "no_std")] use std::prelude::v1::*; use std::{ collections::BTreeMap, fmt, + fmt::Write, hash::Hash, iter::once, num::{NonZeroU8, NonZeroUsize}, @@ -576,6 +580,16 @@ impl Expr { })) } + // Interpolated string + Self::InterpolatedString(x, ..) if self.is_constant() => { + let mut s = SmartString::new_const(); + for segment in x.iter() { + let v = segment.get_literal_value().unwrap(); + write!(&mut s, "{}", v).unwrap(); + } + s.into() + } + // Fn Self::FnCall(ref x, ..) if !x.is_qualified() && x.args.len() == 1 && x.name == KEYWORD_FN_PTR => diff --git a/src/optimizer.rs b/src/optimizer.rs index 590e760d..4f561f65 100644 --- a/src/optimizer.rs +++ b/src/optimizer.rs @@ -12,7 +12,7 @@ use crate::tokenizer::{Span, Token}; use crate::types::dynamic::AccessMode; use crate::{ calc_fn_hash, calc_fn_params_hash, combine_hashes, Dynamic, Engine, FnPtr, Identifier, - Position, Scope, StaticVec, AST, INT, + Position, Scope, StaticVec, AST, INT, ImmutableString, }; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -1052,10 +1052,10 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) { state.set_dirty(); *expr = Expr::StringConstant(state.engine.const_empty_string(), *pos); } - // `...` - Expr::InterpolatedString(x, ..) if x.len() == 1 && matches!(x[0], Expr::StringConstant(..)) => { + // `... ${const} ...` + Expr::InterpolatedString(..) if expr.is_constant() => { state.set_dirty(); - *expr = mem::take(&mut x[0]); + *expr = Expr::StringConstant(expr.get_literal_value().unwrap().cast::(), expr.position()); } // `... ${ ... } ...` Expr::InterpolatedString(x, ..) => { diff --git a/tests/string.rs b/tests/string.rs index c569b880..3bbed302 100644 --- a/tests/string.rs +++ b/tests/string.rs @@ -334,6 +334,8 @@ fn test_string_split() -> Result<(), Box> { fn test_string_interpolated() -> Result<(), Box> { let engine = Engine::new(); + assert_eq!(engine.eval::("`${}`")?, ""); + assert_eq!( engine.eval::( "