diff --git a/src/ast/ast.rs b/src/ast/ast.rs index 0ccad835..06fe65ec 100644 --- a/src/ast/ast.rs +++ b/src/ast/ast.rs @@ -879,7 +879,7 @@ impl Eq for ASTNode<'_> {} impl ASTNode<'_> { /// Get the [`Position`] of this [`ASTNode`]. - pub const fn position(&self) -> Position { + pub fn position(&self) -> Position { match self { ASTNode::Stmt(stmt) => stmt.position(), ASTNode::Expr(expr) => expr.position(), diff --git a/src/ast/expr.rs b/src/ast/expr.rs index 12632325..af1fd390 100644 --- a/src/ast/expr.rs +++ b/src/ast/expr.rs @@ -440,7 +440,7 @@ impl Default for Expr { impl fmt::Debug for Expr { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut display_pos = self.start_position(); + let mut display_pos = format!(" @ {:?}", self.start_position()); match self { Self::DynamicConstant(value, ..) => write!(f, "{:?}", value), @@ -470,24 +470,34 @@ impl fmt::Debug for Expr { f.write_str("Variable(")?; #[cfg(not(feature = "no_module"))] - if let Some((.., ref namespace)) = x.1 { - write!(f, "{}{}", namespace, Token::DoubleColon.literal_syntax())? + if let Some((ref namespace, ..)) = x.1 { + write!(f, "{}{}", namespace, Token::DoubleColon.literal_syntax())?; + let pos = namespace.position(); + if !pos.is_none() { + display_pos = format!(" @ {:?}", pos); + } } f.write_str(&x.2)?; if let Some(n) = i.map_or_else(|| x.0, |n| NonZeroUsize::new(n.get() as usize)) { - write!(f, " #{}", n)? + write!(f, " #{}", n)?; } f.write_str(")") } Self::Property(x, ..) => write!(f, "Property({})", x.2), Self::Stack(x, ..) => write!(f, "ConstantArg[{}]", x), Self::Stmt(x) => { + let pos = x.span(); + if !pos.is_none() { + display_pos = format!(" @ {:?}", pos); + } f.write_str("ExprStmtBlock")?; f.debug_list().entries(x.iter()).finish() } Self::FnCall(x, ..) => fmt::Debug::fmt(x, f), Self::Index(x, term, pos) => { - display_pos = *pos; + if !pos.is_none() { + display_pos = format!(" @ {:?}", pos); + } f.debug_struct("Index") .field("lhs", &x.lhs) @@ -506,7 +516,9 @@ impl fmt::Debug for Expr { ), }; - display_pos = *pos; + if !pos.is_none() { + display_pos = format!(" @ {:?}", pos); + } f.debug_struct(op_name) .field("lhs", &x.lhs) @@ -516,7 +528,7 @@ impl fmt::Debug for Expr { Self::Custom(x, ..) => f.debug_tuple("Custom").field(x).finish(), }?; - display_pos.debug_print(f) + f.write_str(&display_pos) } } @@ -708,8 +720,16 @@ impl Expr { /// For a binary expression, this will be the left-most LHS instead of the operator. #[inline] #[must_use] - pub const fn start_position(&self) -> Position { + pub fn start_position(&self) -> Position { match self { + #[cfg(not(feature = "no_module"))] + Self::Variable(.., x) => { + if let Some((ref namespace, ..)) = x.1 { + namespace.position() + } else { + self.position() + } + } Self::And(x, ..) | Self::Or(x, ..) | Self::Index(x, ..) | Self::Dot(x, ..) => { x.lhs.start_position() } diff --git a/src/ast/stmt.rs b/src/ast/stmt.rs index 97cf1eb0..27962878 100644 --- a/src/ast/stmt.rs +++ b/src/ast/stmt.rs @@ -402,7 +402,7 @@ impl Stmt { } /// Get the [position][Position] of this statement. #[must_use] - pub const fn position(&self) -> Position { + pub fn position(&self) -> Position { match self { Self::Noop(pos) | Self::BreakLoop(.., pos) diff --git a/src/bin/rhai-dbg.rs b/src/bin/rhai-dbg.rs index cf9077b9..eb1c419b 100644 --- a/src/bin/rhai-dbg.rs +++ b/src/bin/rhai-dbg.rs @@ -325,7 +325,13 @@ fn main() { exit(0); } ["node", ..] => { - println!("{:?} {}@{:?}", node, source.unwrap_or_default(), pos); + if pos.is_none() { + println!("{:?}", node); + } else if let Some(source) = source { + println!("{:?} {} @ {:?}", node, source, pos); + } else { + println!("{:?} @ {:?}", node, pos); + } println!(); } ["list" | "l", ..] => print_current_source(context, source, pos, &lines), diff --git a/src/eval/chaining.rs b/src/eval/chaining.rs index a9b05825..f80cf737 100644 --- a/src/eval/chaining.rs +++ b/src/eval/chaining.rs @@ -147,7 +147,6 @@ impl Engine { #[cfg(not(feature = "no_index"))] ChainType::Indexing => { let pos = rhs.start_position(); - let root_pos = idx_val.position(); let idx_val = idx_val.into_index_value().expect("`ChainType::Index`"); match rhs { @@ -190,7 +189,7 @@ impl Engine { ) .or_else(|idx_err| match *idx_err { ERR::ErrorIndexingType(..) => Ok((Dynamic::UNIT, false)), - _ => Err(idx_err.fill_position(root_pos)), + _ => Err(idx_err), })?; } @@ -231,8 +230,7 @@ impl Engine { let new_val = &mut new_val; self.call_indexer_set( global, state, lib, target, idx, new_val, is_ref_mut, level, - ) - .map_err(|err| err.fill_position(root_pos))?; + )?; } Ok((Dynamic::UNIT, true)) @@ -506,7 +504,7 @@ impl Engine { .map_err( |idx_err| match *idx_err { ERR::ErrorIndexingType(..) => err, - _ => idx_err.fill_position(pos), + _ => idx_err, }, ) } @@ -553,12 +551,12 @@ impl Engine { is_ref_mut, level, ) .or_else(|idx_err| match *idx_err { + // If there is no setter, no need to feed it + // back because the property is read-only ERR::ErrorIndexingType(..) => { - // If there is no setter, no need to feed it back because - // the property is read-only Ok((Dynamic::UNIT, false)) } - _ => Err(idx_err.fill_position(pos)), + _ => Err(idx_err), }) } _ => Err(err), @@ -872,7 +870,7 @@ impl Engine { lib: &[&Module], target: &'t mut Dynamic, idx: Dynamic, - pos: Position, + idx_pos: Position, add_if_not_found: bool, use_indexers: bool, level: usize, @@ -889,10 +887,10 @@ impl Engine { // val_array[idx] let index = idx .as_int() - .map_err(|typ| self.make_type_mismatch_err::(typ, pos))?; + .map_err(|typ| self.make_type_mismatch_err::(typ, idx_pos))?; let len = arr.len(); let arr_idx = super::calc_index(len, index, true, || { - ERR::ErrorArrayBounds(len, index, pos).into() + ERR::ErrorArrayBounds(len, index, idx_pos).into() })?; Ok(arr.get_mut(arr_idx).map(Target::from).unwrap()) @@ -903,10 +901,10 @@ impl Engine { // val_blob[idx] let index = idx .as_int() - .map_err(|typ| self.make_type_mismatch_err::(typ, pos))?; + .map_err(|typ| self.make_type_mismatch_err::(typ, idx_pos))?; let len = arr.len(); let arr_idx = super::calc_index(len, index, true, || { - ERR::ErrorArrayBounds(len, index, pos).into() + ERR::ErrorArrayBounds(len, index, idx_pos).into() })?; let value = arr.get(arr_idx).map(|&v| (v as crate::INT).into()).unwrap(); @@ -922,7 +920,7 @@ impl Engine { Dynamic(Union::Map(map, ..)) => { // val_map[idx] let index = idx.read_lock::().ok_or_else(|| { - self.make_type_mismatch_err::(idx.type_name(), pos) + self.make_type_mismatch_err::(idx.type_name(), idx_pos) })?; if _add_if_not_found && !map.contains_key(index.as_str()) { @@ -932,7 +930,7 @@ impl Engine { if let Some(value) = map.get_mut(index.as_str()) { Ok(Target::from(value)) } else if self.fail_on_invalid_map_property() { - Err(ERR::ErrorPropertyNotFound(index.to_string(), pos).into()) + Err(ERR::ErrorPropertyNotFound(index.to_string(), idx_pos).into()) } else { Ok(Target::from(Dynamic::UNIT)) } @@ -950,10 +948,10 @@ impl Engine { let end = range.end; let start = super::calc_index(BITS, start, false, || { - ERR::ErrorBitFieldBounds(BITS, start, pos).into() + ERR::ErrorBitFieldBounds(BITS, start, idx_pos).into() })?; let end = super::calc_index(BITS, end, false, || { - ERR::ErrorBitFieldBounds(BITS, end, pos).into() + ERR::ErrorBitFieldBounds(BITS, end, idx_pos).into() })?; if end <= start { @@ -975,10 +973,10 @@ impl Engine { let end = *range.end(); let start = super::calc_index(BITS, start, false, || { - ERR::ErrorBitFieldBounds(BITS, start, pos).into() + ERR::ErrorBitFieldBounds(BITS, start, idx_pos).into() })?; let end = super::calc_index(BITS, end, false, || { - ERR::ErrorBitFieldBounds(BITS, end, pos).into() + ERR::ErrorBitFieldBounds(BITS, end, idx_pos).into() })?; if end < start { @@ -1014,12 +1012,12 @@ impl Engine { // val_int[idx] let index = idx .as_int() - .map_err(|typ| self.make_type_mismatch_err::(typ, pos))?; + .map_err(|typ| self.make_type_mismatch_err::(typ, idx_pos))?; const BITS: usize = std::mem::size_of::() * 8; let bit = super::calc_index(BITS, index, true, || { - ERR::ErrorBitFieldBounds(BITS, index, pos).into() + ERR::ErrorBitFieldBounds(BITS, index, idx_pos).into() })?; let bit_value = (*value & (1 << bit)) != 0; @@ -1036,14 +1034,14 @@ impl Engine { // val_string[idx] let index = idx .as_int() - .map_err(|typ| self.make_type_mismatch_err::(typ, pos))?; + .map_err(|typ| self.make_type_mismatch_err::(typ, idx_pos))?; let (ch, offset) = if index >= 0 { let offset = index as usize; ( s.chars().nth(offset).ok_or_else(|| { let chars_len = s.chars().count(); - ERR::ErrorStringBounds(chars_len, index, pos) + ERR::ErrorStringBounds(chars_len, index, idx_pos) })?, offset, ) @@ -1053,13 +1051,13 @@ impl Engine { // Count from end if negative s.chars().rev().nth(offset - 1).ok_or_else(|| { let chars_len = s.chars().count(); - ERR::ErrorStringBounds(chars_len, index, pos) + ERR::ErrorStringBounds(chars_len, index, idx_pos) })?, offset, ) } else { let chars_len = s.chars().count(); - return Err(ERR::ErrorStringBounds(chars_len, index, pos).into()); + return Err(ERR::ErrorStringBounds(chars_len, index, idx_pos).into()); }; Ok(Target::StringChar { diff --git a/src/eval/expr.rs b/src/eval/expr.rs index f0e32227..6a9004e8 100644 --- a/src/eval/expr.rs +++ b/src/eval/expr.rs @@ -327,8 +327,7 @@ impl Engine { Expr::Unit(..) => Ok(Dynamic::UNIT), // `... ${...} ...` - Expr::InterpolatedString(x, pos) => { - let mut pos = *pos; + Expr::InterpolatedString(x, _) => { let mut concat: Dynamic = self.const_empty_string().into(); let mut result = Ok(Dynamic::UNIT); @@ -347,7 +346,7 @@ impl Engine { state, lib, Some(OpAssignment::new(OP_CONCAT)), - pos, + expr.start_position(), &mut (&mut concat).into(), ("", Position::NONE), item, @@ -356,8 +355,6 @@ impl Engine { result = Err(err.fill_position(expr.start_position())); break; } - - pos = expr.start_position(); } result.map(|_| concat) diff --git a/src/module/namespace.rs b/src/module/namespace.rs index 1318467f..c2977030 100644 --- a/src/module/namespace.rs +++ b/src/module/namespace.rs @@ -3,7 +3,7 @@ use crate::ast::Ident; use crate::tokenizer::Token; -use crate::StaticVec; +use crate::{Position, StaticVec}; #[cfg(feature = "no_std")] use std::prelude::v1::*; use std::{ @@ -114,4 +114,12 @@ impl Namespace { pub(crate) fn set_index(&mut self, index: Option) { self.index = index } + /// Get the [position][Position] of this [`NameSpace`]. + /// + /// # Panics + /// + /// Panics if the path is empty. + pub fn position(&self) -> Position { + self.path[0].pos + } } diff --git a/src/optimizer.rs b/src/optimizer.rs index 496937a5..220fae23 100644 --- a/src/optimizer.rs +++ b/src/optimizer.rs @@ -940,7 +940,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) { while n < x.len() - 1 { match (mem::take(&mut x[n]), mem::take(&mut x[n+1])) { (Expr::StringConstant(mut s1, pos), Expr::StringConstant(s2, ..)) => { s1 += s2; x[n] = Expr::StringConstant(s1, pos); x.remove(n+1); state.set_dirty(); } - (expr1, Expr::Unit(..)) => { x[n] = expr1; x.remove(n+1); state.set_dirty(); } + (expr1, Expr::Unit(..)) => { x[n] = expr1; x.remove(n+1); state.set_dirty(); } (Expr::Unit(..), expr2) => { x[n+1] = expr2; x.remove(n); state.set_dirty(); } (expr1, Expr::StringConstant(s, ..)) if s.is_empty() => { x[n] = expr1; x.remove(n+1); state.set_dirty(); } (Expr::StringConstant(s, ..), expr2) if s.is_empty()=> { x[n+1] = expr2; x.remove(n); state.set_dirty(); } diff --git a/src/parser.rs b/src/parser.rs index b2f95d93..b57eed9a 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1291,8 +1291,9 @@ fn parse_primary( let mut segments = StaticVec::::new(); match input.next().expect(NEVER_ENDS) { + (Token::InterpolatedString(s), ..) if s.is_empty() => (), (Token::InterpolatedString(s), pos) => { - segments.push(Expr::StringConstant(s.into(), pos)); + segments.push(Expr::StringConstant(s.into(), pos)) } token => unreachable!("Token::InterpolatedString expected but gets {:?}", token), } @@ -1302,7 +1303,10 @@ fn parse_primary( block @ Stmt::Block(..) => Expr::Stmt(Box::new(block.into())), stmt => unreachable!("Stmt::Block expected but gets {:?}", stmt), }; - segments.push(expr); + match expr { + Expr::StringConstant(s, ..) if s.is_empty() => (), + _ => segments.push(expr), + } // Make sure to parse the following as text let mut control = state.tokenizer_control.get(); @@ -1332,6 +1336,12 @@ fn parse_primary( } } + if segments.is_empty() { + segments.push(Expr::StringConstant( + state.get_interned_string("", ""), + settings.pos, + )); + } segments.shrink_to_fit(); Expr::InterpolatedString(segments.into(), settings.pos) } diff --git a/src/tokenizer.rs b/src/tokenizer.rs index fa427351..9857bf78 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -257,16 +257,19 @@ impl fmt::Display for Position { impl fmt::Debug for Position { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - #[cfg(not(feature = "no_position"))] - if self.is_beginning_of_line() { - write!(f, "{}", self.line)?; + if self.is_none() { + f.write_str("none") } else { - write!(f, "{}:{}", self.line, self.pos)?; - } - #[cfg(feature = "no_position")] - f.write_str("none")?; + #[cfg(not(feature = "no_position"))] + if self.is_beginning_of_line() { + write!(f, "{}", self.line) + } else { + write!(f, "{}:{}", self.line, self.pos) + } - Ok(()) + #[cfg(feature = "no_position")] + unreachable!(); + } } }