Improve position display.

This commit is contained in:
Stephen Chung 2022-02-04 12:04:33 +08:00
parent 345a060672
commit 6c1c8bc538
11 changed files with 212 additions and 138 deletions

View File

@ -35,6 +35,9 @@ Enhancements
* Debug display of `AST` is improved. * Debug display of `AST` is improved.
* `NativeCallContext::call_level()` is added to give the current nesting level of function calls. * `NativeCallContext::call_level()` is added to give the current nesting level of function calls.
* A new feature, `bin-features`, pulls in all the required features for `bin` tools. * A new feature, `bin-features`, pulls in all the required features for `bin` tools.
* `AST` position display is improved:
* `Expr::start_position` is added to give the beginning of the expression (not the operator's position).
* `StmtBlock` and `Stmt::Block` now keep the position of the closing `}` as well.
REPL tool changes REPL tool changes
----------------- -----------------

View File

@ -91,7 +91,7 @@ impl AST {
) -> Self { ) -> Self {
Self { Self {
source: Identifier::new_const(), source: Identifier::new_const(),
body: StmtBlock::new(statements, Position::NONE), body: StmtBlock::new(statements, Position::NONE, Position::NONE),
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
lib: functions.into(), lib: functions.into(),
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]

View File

@ -191,6 +191,8 @@ pub struct FnCallExpr {
pub constants: StaticVec<Dynamic>, pub constants: StaticVec<Dynamic>,
/// Does this function call capture the parent scope? /// Does this function call capture the parent scope?
pub capture_parent_scope: bool, pub capture_parent_scope: bool,
/// [Position] of the function name.
pub pos: Position,
} }
impl fmt::Debug for FnCallExpr { impl fmt::Debug for FnCallExpr {
@ -207,6 +209,7 @@ impl fmt::Debug for FnCallExpr {
if self.capture_parent_scope { if self.capture_parent_scope {
ff.field("capture_parent_scope", &self.capture_parent_scope); ff.field("capture_parent_scope", &self.capture_parent_scope);
} }
ff.field("pos", &self.pos);
ff.finish() ff.finish()
} }
} }
@ -437,7 +440,7 @@ impl Default for Expr {
impl fmt::Debug for Expr { impl fmt::Debug for Expr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut display_pos = self.position(); let mut display_pos = self.start_position();
match self { match self {
Self::DynamicConstant(value, _) => write!(f, "{:?}", value), Self::DynamicConstant(value, _) => write!(f, "{:?}", value),
@ -629,6 +632,7 @@ impl Expr {
args: once(Self::Stack(0, pos)).collect(), args: once(Self::Stack(0, pos)).collect(),
constants: once(f.fn_name().into()).collect(), constants: once(f.fn_name().into()).collect(),
capture_parent_scope: false, capture_parent_scope: false,
pos,
} }
.into(), .into(),
pos, pos,
@ -685,15 +689,30 @@ impl Expr {
| Self::Map(_, pos) | Self::Map(_, pos)
| Self::Variable(_, pos, _) | Self::Variable(_, pos, _)
| Self::Stack(_, pos) | Self::Stack(_, pos)
| Self::FnCall(_, pos) | Self::And(_, pos)
| Self::Or(_, pos)
| Self::Index(_, _, pos) | Self::Index(_, _, pos)
| Self::Dot(_, _, pos)
| Self::Custom(_, pos) | Self::Custom(_, pos)
| Self::InterpolatedString(_, pos) | Self::InterpolatedString(_, pos)
| Self::Property(_, pos) => *pos, | Self::Property(_, pos) => *pos,
Self::Stmt(x) => x.position(), Self::FnCall(x, _) => x.pos,
Self::And(x, _) | Self::Or(x, _) | Self::Dot(x, _, _) => x.lhs.position(), Self::Stmt(x) => x.position(),
}
}
/// Get the starting [position][Position] of the expression.
/// 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 {
match self {
Self::And(x, _) | Self::Or(x, _) | Self::Index(x, _, _) | Self::Dot(x, _, _) => {
x.lhs.start_position()
}
Self::FnCall(_, pos) => *pos,
_ => self.position(),
} }
} }
/// Override the [position][Position] of the expression. /// Override the [position][Position] of the expression.
@ -722,7 +741,7 @@ impl Expr {
| Self::InterpolatedString(_, pos) | Self::InterpolatedString(_, pos)
| Self::Property(_, pos) => *pos = new_pos, | Self::Property(_, pos) => *pos = new_pos,
Self::Stmt(x) => x.set_position(new_pos), Self::Stmt(x) => x.set_position(new_pos, Position::NONE),
} }
self self

View File

@ -134,7 +134,7 @@ pub struct TryCatchBlock {
/// _(internals)_ A scoped block of statements. /// _(internals)_ A scoped block of statements.
/// Exported under the `internals` feature only. /// Exported under the `internals` feature only.
#[derive(Clone, Hash, Default)] #[derive(Clone, Hash, Default)]
pub struct StmtBlock(StaticVec<Stmt>, Position); pub struct StmtBlock(StaticVec<Stmt>, (Position, Position));
impl StmtBlock { impl StmtBlock {
/// A [`StmtBlock`] that does not exist. /// A [`StmtBlock`] that does not exist.
@ -142,16 +142,20 @@ impl StmtBlock {
/// Create a new [`StmtBlock`]. /// Create a new [`StmtBlock`].
#[must_use] #[must_use]
pub fn new(statements: impl IntoIterator<Item = Stmt>, pos: Position) -> Self { pub fn new(
statements: impl IntoIterator<Item = Stmt>,
start_pos: Position,
end_pos: Position,
) -> Self {
let mut statements: StaticVec<_> = statements.into_iter().collect(); let mut statements: StaticVec<_> = statements.into_iter().collect();
statements.shrink_to_fit(); statements.shrink_to_fit();
Self(statements, pos) Self(statements, (start_pos, end_pos))
} }
/// Create an empty [`StmtBlock`]. /// Create an empty [`StmtBlock`].
#[inline(always)] #[inline(always)]
#[must_use] #[must_use]
pub const fn empty(pos: Position) -> Self { pub const fn empty(pos: Position) -> Self {
Self(StaticVec::new_const(), pos) Self(StaticVec::new_const(), (pos, pos))
} }
/// Is this statements block empty? /// Is this statements block empty?
#[inline(always)] #[inline(always)]
@ -183,16 +187,42 @@ impl StmtBlock {
pub fn iter(&self) -> impl Iterator<Item = &Stmt> { pub fn iter(&self) -> impl Iterator<Item = &Stmt> {
self.0.iter() self.0.iter()
} }
/// Get the position (location of the beginning `{`) of this statements block. /// Get the start position (location of the beginning `{`) of this statements block.
#[inline(always)] #[inline(always)]
#[must_use] #[must_use]
pub const fn position(&self) -> Position { pub const fn position(&self) -> Position {
(self.1).0
}
/// Get the end position (location of the ending `}`) of this statements block.
#[inline(always)]
#[must_use]
pub const fn end_position(&self) -> Position {
(self.1).1
}
/// Get the positions (locations of the beginning `{` and ending `}`) of this statements block.
#[inline(always)]
#[must_use]
pub const fn positions(&self) -> (Position, Position) {
self.1 self.1
} }
/// Set the position (location of the beginning `{`) of this statements block. /// Get the positions (locations of the beginning `{` and ending `}`) of this statements block
/// or a default.
#[inline(always)] #[inline(always)]
pub fn set_position(&mut self, pos: Position) { #[must_use]
self.1 = pos; pub const fn positions_or_else(
&self,
def_start_pos: Position,
def_end_pos: Position,
) -> (Position, Position) {
(
(self.1).0.or_else(def_start_pos),
(self.1).1.or_else(def_end_pos),
)
}
/// Set the positions of this statements block.
#[inline(always)]
pub fn set_position(&mut self, start_pos: Position, end_pos: Position) {
self.1 = (start_pos, end_pos);
} }
} }
@ -230,7 +260,12 @@ impl fmt::Debug for StmtBlock {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("Block")?; f.write_str("Block")?;
fmt::Debug::fmt(&self.0, f)?; fmt::Debug::fmt(&self.0, f)?;
self.1.debug_print(f) (self.1).0.debug_print(f)?;
#[cfg(not(feature = "no_position"))]
if !(self.1).1.is_none() {
write!(f, "-{:?}", (self.1).1)?;
}
Ok(())
} }
} }
@ -239,10 +274,10 @@ impl From<Stmt> for StmtBlock {
fn from(stmt: Stmt) -> Self { fn from(stmt: Stmt) -> Self {
match stmt { match stmt {
Stmt::Block(mut block, pos) => Self(block.iter_mut().map(mem::take).collect(), pos), Stmt::Block(mut block, pos) => Self(block.iter_mut().map(mem::take).collect(), pos),
Stmt::Noop(pos) => Self(StaticVec::new_const(), pos), Stmt::Noop(pos) => Self(StaticVec::new_const(), (pos, pos)),
_ => { _ => {
let pos = stmt.position(); let pos = stmt.position();
Self(vec![stmt].into(), pos) Self(vec![stmt].into(), (pos, Position::NONE))
} }
} }
} }
@ -309,7 +344,7 @@ pub enum Stmt {
/// function call forming one statement. /// function call forming one statement.
FnCall(Box<FnCallExpr>, Position), FnCall(Box<FnCallExpr>, Position),
/// `{` stmt`;` ... `}` /// `{` stmt`;` ... `}`
Block(Box<[Stmt]>, Position), Block(Box<[Stmt]>, (Position, Position)),
/// `try` `{` stmt; ... `}` `catch` `(` var `)` `{` stmt; ... `}` /// `try` `{` stmt; ... `}` `catch` `(` var `)` `{` stmt; ... `}`
TryCatch(Box<TryCatchBlock>, Position), TryCatch(Box<TryCatchBlock>, Position),
/// [expression][Expr] /// [expression][Expr]
@ -377,7 +412,7 @@ impl Stmt {
match self { match self {
Self::Noop(pos) Self::Noop(pos)
| Self::BreakLoop(_, pos) | Self::BreakLoop(_, pos)
| Self::Block(_, pos) | Self::Block(_, (pos, _))
| Self::Assignment(_, pos) | Self::Assignment(_, pos)
| Self::FnCall(_, pos) | Self::FnCall(_, pos)
| Self::If(_, _, pos) | Self::If(_, _, pos)
@ -389,7 +424,7 @@ impl Stmt {
| Self::Var(_, _, _, pos) | Self::Var(_, _, _, pos)
| Self::TryCatch(_, pos) => *pos, | Self::TryCatch(_, pos) => *pos,
Self::Expr(x) => x.position(), Self::Expr(x) => x.start_position(),
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
Self::Import(_, _, pos) => *pos, Self::Import(_, _, pos) => *pos,
@ -405,7 +440,7 @@ impl Stmt {
match self { match self {
Self::Noop(pos) Self::Noop(pos)
| Self::BreakLoop(_, pos) | Self::BreakLoop(_, pos)
| Self::Block(_, pos) | Self::Block(_, (pos, _))
| Self::Assignment(_, pos) | Self::Assignment(_, pos)
| Self::FnCall(_, pos) | Self::FnCall(_, pos)
| Self::If(_, _, pos) | Self::If(_, _, pos)

View File

@ -146,7 +146,7 @@ impl Engine {
match chain_type { match chain_type {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
ChainType::Indexing => { ChainType::Indexing => {
let pos = rhs.position(); let pos = rhs.start_position();
let root_pos = idx_val.position(); let root_pos = idx_val.position();
let idx_val = idx_val.into_index_value().expect("`ChainType::Index`"); let idx_val = idx_val.into_index_value().expect("`ChainType::Index`");
@ -159,7 +159,7 @@ impl Engine {
self.run_debugger(scope, global, state, lib, this_ptr, _parent, level)?; self.run_debugger(scope, global, state, lib, this_ptr, _parent, level)?;
let mut idx_val_for_setter = idx_val.clone(); let mut idx_val_for_setter = idx_val.clone();
let idx_pos = x.lhs.position(); let idx_pos = x.lhs.start_position();
let rhs_chain = rhs.into(); let rhs_chain = rhs.into();
let (try_setter, result) = { let (try_setter, result) = {
@ -629,7 +629,7 @@ impl Engine {
} }
} }
// Syntax error // Syntax error
_ => Err(ERR::ErrorDotExpr("".into(), rhs.position()).into()), _ => Err(ERR::ErrorDotExpr("".into(), rhs.start_position()).into()),
} }
} }
} }
@ -691,7 +691,7 @@ impl Engine {
expr => { expr => {
let value = self.eval_expr(scope, global, state, lib, this_ptr, expr, level)?; let value = self.eval_expr(scope, global, state, lib, this_ptr, expr, level)?;
let obj_ptr = &mut value.into(); let obj_ptr = &mut value.into();
let root = ("", expr.position()); let root = ("", expr.start_position());
self.eval_dot_index_chain_helper( self.eval_dot_index_chain_helper(
global, state, lib, this_ptr, obj_ptr, root, expr, rhs, term, idx_values, global, state, lib, this_ptr, obj_ptr, root, expr, rhs, term, idx_values,
chain_type, level, new_val, chain_type, level, new_val,
@ -804,7 +804,10 @@ impl Engine {
_ if _parent_chain_type == ChainType::Indexing => self _ if _parent_chain_type == ChainType::Indexing => self
.eval_expr(scope, global, state, lib, this_ptr, lhs, level) .eval_expr(scope, global, state, lib, this_ptr, lhs, level)
.map(|v| { .map(|v| {
super::ChainArgument::from_index_value(v.flatten(), lhs.position()) super::ChainArgument::from_index_value(
v.flatten(),
lhs.start_position(),
)
})?, })?,
expr => unreachable!("unknown chained expression: {:?}", expr), expr => unreachable!("unknown chained expression: {:?}", expr),
}; };
@ -828,7 +831,7 @@ impl Engine {
_ if _parent_chain_type == ChainType::Indexing => idx_values.push( _ if _parent_chain_type == ChainType::Indexing => idx_values.push(
self.eval_expr(scope, global, state, lib, this_ptr, expr, level) self.eval_expr(scope, global, state, lib, this_ptr, expr, level)
.map(|v| { .map(|v| {
super::ChainArgument::from_index_value(v.flatten(), expr.position()) super::ChainArgument::from_index_value(v.flatten(), expr.start_position())
})?, })?,
), ),
_ => unreachable!("unknown chained expression: {:?}", expr), _ => unreachable!("unknown chained expression: {:?}", expr),

View File

@ -148,7 +148,7 @@ impl Engine {
Err(ERR::ErrorUnboundThis(*pos).into()) Err(ERR::ErrorUnboundThis(*pos).into())
} }
} }
_ if state.always_search_scope => (0, expr.position()), _ if state.always_search_scope => (0, expr.start_position()),
Expr::Variable(Some(i), pos, _) => (i.get() as usize, *pos), Expr::Variable(Some(i), pos, _) => (i.get() as usize, *pos),
Expr::Variable(None, pos, v) => (v.0.map(NonZeroUsize::get).unwrap_or(0), *pos), Expr::Variable(None, pos, v) => (v.0.map(NonZeroUsize::get).unwrap_or(0), *pos),
_ => unreachable!("Expr::Variable expected but gets {:?}", expr), _ => unreachable!("Expr::Variable expected but gets {:?}", expr),
@ -347,11 +347,11 @@ impl Engine {
item, item,
level, level,
) { ) {
result = Err(err.fill_position(expr.position())); result = Err(err.fill_position(expr.start_position()));
break; break;
} }
pos = expr.position(); pos = expr.start_position();
} }
result.map(|_| concat) result.map(|_| concat)
@ -504,7 +504,7 @@ impl Engine {
let result = (custom_def.func)(&mut context, &expressions); let result = (custom_def.func)(&mut context, &expressions);
self.check_return_value(result, expr.position()) self.check_return_value(result, expr.start_position())
} }
Expr::Stmt(x) if x.is_empty() => Ok(Dynamic::UNIT), Expr::Stmt(x) if x.is_empty() => Ok(Dynamic::UNIT),

View File

@ -267,7 +267,7 @@ impl Engine {
rhs_val, rhs_val,
level, level,
) )
.map_err(|err| err.fill_position(rhs.position())) .map_err(|err| err.fill_position(rhs.start_position()))
.map(|_| Dynamic::UNIT) .map(|_| Dynamic::UNIT)
} else { } else {
search_result.map(|_| Dynamic::UNIT) search_result.map(|_| Dynamic::UNIT)
@ -283,7 +283,7 @@ impl Engine {
.map(Dynamic::flatten); .map(Dynamic::flatten);
if let Ok(rhs_val) = rhs_result { if let Ok(rhs_val) = rhs_result {
let _new_val = Some(((rhs_val, rhs.position()), (*op_info, *op_pos))); let _new_val = Some(((rhs_val, rhs.start_position()), (*op_info, *op_pos)));
// Must be either `var[index] op= val` or `var.prop op= val` // Must be either `var[index] op= val` or `var.prop op= val`
match lhs { match lhs {
@ -686,7 +686,7 @@ impl Engine {
loop_result loop_result
} else { } else {
Err(ERR::ErrorFor(expr.position()).into()) Err(ERR::ErrorFor(expr.start_position()).into())
} }
} else { } else {
iter_result iter_result
@ -869,9 +869,10 @@ impl Engine {
let path_result = self let path_result = self
.eval_expr(scope, global, state, lib, this_ptr, &expr, level) .eval_expr(scope, global, state, lib, this_ptr, &expr, level)
.and_then(|v| { .and_then(|v| {
let typ = v.type_name();
v.try_cast::<crate::ImmutableString>().ok_or_else(|| { v.try_cast::<crate::ImmutableString>().ok_or_else(|| {
self.make_type_mismatch_err::<crate::ImmutableString>( self.make_type_mismatch_err::<crate::ImmutableString>(
"", typ,
expr.position(), expr.position(),
) )
}) })
@ -880,7 +881,7 @@ impl Engine {
if let Ok(path) = path_result { if let Ok(path) = path_result {
use crate::ModuleResolver; use crate::ModuleResolver;
let path_pos = expr.position(); let path_pos = expr.start_position();
let resolver = global.embedded_module_resolver.clone(); let resolver = global.embedded_module_resolver.clone();

View File

@ -426,23 +426,26 @@ impl Engine {
}; };
#[cfg(feature = "debugging")] #[cfg(feature = "debugging")]
if self.debugger.is_some() { {
match global.debugger.status { let trigger = match global.debugger.status {
crate::eval::DebuggerStatus::FunctionExit(n) if n >= level => { crate::eval::DebuggerStatus::FunctionExit(n) => n >= level,
let scope = &mut &mut Scope::new(); crate::eval::DebuggerStatus::Next(_, true) => true,
let node = crate::ast::Stmt::Noop(pos); _ => false,
let node = (&node).into(); };
let event = match _result { if trigger {
Ok(ref r) => crate::eval::DebuggerEvent::FunctionExitWithValue(r), let scope = &mut &mut Scope::new();
Err(ref err) => crate::eval::DebuggerEvent::FunctionExitWithError(err), let node = crate::ast::Stmt::Noop(pos);
}; let node = (&node).into();
if let Err(err) = self.run_debugger_raw( let event = match _result {
scope, global, state, lib, &mut None, node, event, level, Ok(ref r) => crate::eval::DebuggerEvent::FunctionExitWithValue(r),
) { Err(ref err) => crate::eval::DebuggerEvent::FunctionExitWithError(err),
_result = Err(err); };
} match self
.run_debugger_raw(scope, global, state, lib, &mut None, node, event, level)
{
Ok(_) => (),
Err(err) => _result = Err(err),
} }
_ => (),
} }
// Pop the call stack // Pop the call stack
@ -977,7 +980,7 @@ impl Engine {
result? result?
}, },
arg_expr.position(), arg_expr.start_position(),
)) ))
} }
@ -1378,24 +1381,17 @@ impl Engine {
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
Some(f) if f.is_script() => { Some(f) if f.is_script() => {
let fn_def = f.get_script_fn_def().expect("script-defined function"); let fn_def = f.get_script_fn_def().expect("script-defined function");
let new_scope = &mut Scope::new();
let mut source = module.id_raw().clone();
mem::swap(&mut global.source, &mut source);
if fn_def.body.is_empty() { let result = self.call_script_fn(
Ok(Dynamic::UNIT) new_scope, global, state, lib, &mut None, fn_def, &mut args, true, pos, level,
} else { );
let new_scope = &mut Scope::new();
let mut source = module.id_raw().clone(); global.source = source;
mem::swap(&mut global.source, &mut source);
let result = self.call_script_fn( result
new_scope, global, state, lib, &mut None, fn_def, &mut args, true, pos,
level,
);
global.source = source;
result
}
} }
Some(f) if f.is_plugin_fn() => { Some(f) if f.is_plugin_fn() => {

View File

@ -182,22 +182,24 @@ impl Engine {
}); });
#[cfg(feature = "debugging")] #[cfg(feature = "debugging")]
if self.debugger.is_some() { {
match global.debugger.status { let trigger = match global.debugger.status {
crate::eval::DebuggerStatus::FunctionExit(n) if n >= level => { crate::eval::DebuggerStatus::FunctionExit(n) => n >= level,
let node = crate::ast::Stmt::Noop(pos); crate::eval::DebuggerStatus::Next(_, true) => true,
let node = (&node).into(); _ => false,
let event = match _result { };
Ok(ref r) => crate::eval::DebuggerEvent::FunctionExitWithValue(r), if trigger {
Err(ref err) => crate::eval::DebuggerEvent::FunctionExitWithError(err), let node = crate::ast::Stmt::Noop(fn_def.body.end_position().or_else(pos));
}; let node = (&node).into();
if let Err(err) = self let event = match _result {
.run_debugger_raw(scope, global, state, lib, this_ptr, node, event, level) Ok(ref r) => crate::eval::DebuggerEvent::FunctionExitWithValue(r),
{ Err(ref err) => crate::eval::DebuggerEvent::FunctionExitWithError(err),
_result = Err(err); };
} match self.run_debugger_raw(scope, global, state, lib, this_ptr, node, event, level)
{
Ok(_) => (),
Err(err) => _result = Err(err),
} }
_ => (),
} }
// Pop the call stack // Pop the call stack

View File

@ -463,13 +463,16 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
Stmt::If(condition, x, _) if x.0.is_empty() && x.1.is_empty() => { Stmt::If(condition, x, _) if x.0.is_empty() && x.1.is_empty() => {
state.set_dirty(); state.set_dirty();
let pos = condition.position(); let pos = condition.start_position();
let mut expr = mem::take(condition); let mut expr = mem::take(condition);
optimize_expr(&mut expr, state, false); optimize_expr(&mut expr, state, false);
*stmt = if preserve_result { *stmt = if preserve_result {
// -> { expr, Noop } // -> { expr, Noop }
Stmt::Block([Stmt::Expr(expr), Stmt::Noop(pos)].into(), pos) Stmt::Block(
[Stmt::Expr(expr), Stmt::Noop(pos)].into(),
(pos, Position::NONE),
)
} else { } else {
// -> expr // -> expr
Stmt::Expr(expr) Stmt::Expr(expr)
@ -487,7 +490,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
match optimize_stmt_block(mem::take(&mut *x.1), state, preserve_result, true, false) match optimize_stmt_block(mem::take(&mut *x.1), state, preserve_result, true, false)
{ {
statements if statements.is_empty() => Stmt::Noop(x.1.position()), statements if statements.is_empty() => Stmt::Noop(x.1.position()),
statements => Stmt::Block(statements.into_boxed_slice(), x.1.position()), statements => Stmt::Block(statements.into_boxed_slice(), x.1.positions()),
} }
} }
// if true { if_block } else { else_block } -> if_block // if true { if_block } else { else_block } -> if_block
@ -497,7 +500,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
match optimize_stmt_block(mem::take(&mut *x.0), state, preserve_result, true, false) match optimize_stmt_block(mem::take(&mut *x.0), state, preserve_result, true, false)
{ {
statements if statements.is_empty() => Stmt::Noop(x.0.position()), statements if statements.is_empty() => Stmt::Noop(x.0.position()),
statements => Stmt::Block(statements.into_boxed_slice(), x.0.position()), statements => Stmt::Block(statements.into_boxed_slice(), x.0.positions()),
} }
} }
// if expr { if_block } else { else_block } // if expr { if_block } else { else_block }
@ -531,11 +534,11 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
mem::take(&mut block.statements), mem::take(&mut block.statements),
Stmt::Block( Stmt::Block(
def_stmt.into_boxed_slice(), def_stmt.into_boxed_slice(),
x.def_case.position().or_else(*pos), x.def_case.positions_or_else(*pos, Position::NONE),
) )
.into(), .into(),
)), )),
match_expr.position(), match_expr.start_position(),
); );
} else { } else {
// Promote the matched case // Promote the matched case
@ -546,7 +549,8 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
true, true,
false, false,
); );
*stmt = Stmt::Block(statements.into_boxed_slice(), block.statements.position()); *stmt =
Stmt::Block(statements.into_boxed_slice(), block.statements.positions());
} }
state.set_dirty(); state.set_dirty();
@ -586,11 +590,11 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
mem::take(&mut block.statements), mem::take(&mut block.statements),
Stmt::Block( Stmt::Block(
def_stmt.into_boxed_slice(), def_stmt.into_boxed_slice(),
x.def_case.position().or_else(*pos), x.def_case.positions_or_else(*pos, Position::NONE),
) )
.into(), .into(),
)), )),
match_expr.position(), match_expr.start_position(),
); );
} else { } else {
// Promote the matched case // Promote the matched case
@ -599,7 +603,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
optimize_stmt_block(statements, state, true, true, false); optimize_stmt_block(statements, state, true, true, false);
*stmt = Stmt::Block( *stmt = Stmt::Block(
statements.into_boxed_slice(), statements.into_boxed_slice(),
block.statements.position(), block.statements.positions(),
); );
} }
@ -647,7 +651,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
optimize_stmt_block(mem::take(&mut x.def_case), state, true, true, false); optimize_stmt_block(mem::take(&mut x.def_case), state, true, true, false);
*stmt = Stmt::Block( *stmt = Stmt::Block(
def_stmt.into_boxed_slice(), def_stmt.into_boxed_slice(),
x.def_case.position().or_else(*pos), x.def_case.positions_or_else(*pos, Position::NONE),
); );
} }
// switch // switch
@ -704,7 +708,8 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
if preserve_result { if preserve_result {
statements.push(Stmt::Noop(pos)) statements.push(Stmt::Noop(pos))
} }
*stmt = Stmt::Block(statements.into_boxed_slice(), pos); *stmt =
Stmt::Block(statements.into_boxed_slice(), (pos, Position::NONE));
} else { } else {
*stmt = Stmt::Noop(pos); *stmt = Stmt::Noop(pos);
}; };
@ -721,7 +726,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
*stmt = Stmt::Block( *stmt = Stmt::Block(
optimize_stmt_block(mem::take(&mut **body), state, false, true, false) optimize_stmt_block(mem::take(&mut **body), state, false, true, false)
.into_boxed_slice(), .into_boxed_slice(),
body.position(), body.positions(),
); );
} }
// do { block } while|until expr // do { block } while|until expr
@ -749,7 +754,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
match block.as_mut_slice() { match block.as_mut_slice() {
[] => { [] => {
state.set_dirty(); state.set_dirty();
*stmt = Stmt::Noop(*pos); *stmt = Stmt::Noop(pos.0);
} }
// Only one statement which is not block-dependent - promote // Only one statement which is not block-dependent - promote
[s] if !s.is_block_dependent() => { [s] if !s.is_block_dependent() => {
@ -766,7 +771,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
*stmt = Stmt::Block( *stmt = Stmt::Block(
optimize_stmt_block(mem::take(&mut *x.try_block), state, false, true, false) optimize_stmt_block(mem::take(&mut *x.try_block), state, false, true, false)
.into_boxed_slice(), .into_boxed_slice(),
x.try_block.position(), x.try_block.positions(),
); );
} }
// try { try_block } catch ( var ) { catch_block } // try { try_block } catch ( var ) { catch_block }
@ -1073,7 +1078,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) {
if let Some(value) = arg.get_literal_value() { if let Some(value) = arg.get_literal_value() {
state.set_dirty(); state.set_dirty();
constants.push(value); constants.push(value);
*arg = Expr::Stack(constants.len()-1, arg.position()); *arg = Expr::Stack(constants.len()-1, arg.start_position());
} }
}); });
} }
@ -1121,7 +1126,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) {
if let Some(value) = arg.get_literal_value() { if let Some(value) = arg.get_literal_value() {
state.set_dirty(); state.set_dirty();
x.constants.push(value); x.constants.push(value);
*arg = Expr::Stack(x.constants.len()-1, arg.position()); *arg = Expr::Stack(x.constants.len()-1, arg.start_position());
} }
}, },

View File

@ -306,7 +306,7 @@ impl Expr {
Err( Err(
PERR::MismatchedType("a boolean expression".to_string(), type_name.to_string()) PERR::MismatchedType("a boolean expression".to_string(), type_name.to_string())
.into_err(self.position()), .into_err(self.start_position()),
) )
} }
/// Raise an error if the expression can never yield an iterable value. /// Raise an error if the expression can never yield an iterable value.
@ -326,7 +326,7 @@ impl Expr {
Err( Err(
PERR::MismatchedType("an iterable value".to_string(), type_name.to_string()) PERR::MismatchedType("an iterable value".to_string(), type_name.to_string())
.into_err(self.position()), .into_err(self.start_position()),
) )
} }
} }
@ -521,6 +521,7 @@ fn parse_fn_call(
namespace, namespace,
hashes, hashes,
args, args,
pos: settings.pos,
..Default::default() ..Default::default()
} }
.into_fn_call_expr(settings.pos)); .into_fn_call_expr(settings.pos));
@ -585,6 +586,7 @@ fn parse_fn_call(
namespace, namespace,
hashes, hashes,
args, args,
pos: settings.pos,
..Default::default() ..Default::default()
} }
.into_fn_call_expr(settings.pos)); .into_fn_call_expr(settings.pos));
@ -652,7 +654,7 @@ fn parse_index_chain(
return Err(PERR::MalformedIndexExpr( return Err(PERR::MalformedIndexExpr(
"Only arrays, object maps and strings can be indexed".into(), "Only arrays, object maps and strings can be indexed".into(),
) )
.into_err(lhs.position())) .into_err(lhs.start_position()))
} }
Expr::CharConstant(_, _) Expr::CharConstant(_, _)
@ -663,7 +665,7 @@ fn parse_index_chain(
return Err(PERR::MalformedIndexExpr( return Err(PERR::MalformedIndexExpr(
"Only arrays, object maps and strings can be indexed".into(), "Only arrays, object maps and strings can be indexed".into(),
) )
.into_err(lhs.position())) .into_err(lhs.start_position()))
} }
_ => (), _ => (),
@ -677,7 +679,7 @@ fn parse_index_chain(
return Err(PERR::MalformedIndexExpr( return Err(PERR::MalformedIndexExpr(
"Array or string expects numeric index, not a string".into(), "Array or string expects numeric index, not a string".into(),
) )
.into_err(idx_expr.position())) .into_err(idx_expr.start_position()))
} }
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
@ -685,7 +687,7 @@ fn parse_index_chain(
return Err(PERR::MalformedIndexExpr( return Err(PERR::MalformedIndexExpr(
"Only arrays, object maps and strings can be indexed".into(), "Only arrays, object maps and strings can be indexed".into(),
) )
.into_err(lhs.position())) .into_err(lhs.start_position()))
} }
Expr::CharConstant(_, _) Expr::CharConstant(_, _)
@ -696,7 +698,7 @@ fn parse_index_chain(
return Err(PERR::MalformedIndexExpr( return Err(PERR::MalformedIndexExpr(
"Only arrays, object maps and strings can be indexed".into(), "Only arrays, object maps and strings can be indexed".into(),
) )
.into_err(lhs.position())) .into_err(lhs.start_position()))
} }
_ => (), _ => (),
@ -708,35 +710,35 @@ fn parse_index_chain(
return Err(PERR::MalformedIndexExpr( return Err(PERR::MalformedIndexExpr(
"Array access expects integer index, not a float".into(), "Array access expects integer index, not a float".into(),
) )
.into_err(x.position())) .into_err(x.start_position()))
} }
// lhs[char] // lhs[char]
x @ Expr::CharConstant(_, _) => { x @ Expr::CharConstant(_, _) => {
return Err(PERR::MalformedIndexExpr( return Err(PERR::MalformedIndexExpr(
"Array access expects integer index, not a character".into(), "Array access expects integer index, not a character".into(),
) )
.into_err(x.position())) .into_err(x.start_position()))
} }
// lhs[()] // lhs[()]
x @ Expr::Unit(_) => { x @ Expr::Unit(_) => {
return Err(PERR::MalformedIndexExpr( return Err(PERR::MalformedIndexExpr(
"Array access expects integer index, not ()".into(), "Array access expects integer index, not ()".into(),
) )
.into_err(x.position())) .into_err(x.start_position()))
} }
// lhs[??? && ???], lhs[??? || ???] // lhs[??? && ???], lhs[??? || ???]
x @ Expr::And(_, _) | x @ Expr::Or(_, _) => { x @ Expr::And(_, _) | x @ Expr::Or(_, _) => {
return Err(PERR::MalformedIndexExpr( return Err(PERR::MalformedIndexExpr(
"Array access expects integer index, not a boolean".into(), "Array access expects integer index, not a boolean".into(),
) )
.into_err(x.position())) .into_err(x.start_position()))
} }
// lhs[true], lhs[false] // lhs[true], lhs[false]
x @ Expr::BoolConstant(_, _) => { x @ Expr::BoolConstant(_, _) => {
return Err(PERR::MalformedIndexExpr( return Err(PERR::MalformedIndexExpr(
"Array access expects integer index, not a boolean".into(), "Array access expects integer index, not a boolean".into(),
) )
.into_err(x.position())) .into_err(x.start_position()))
} }
// All other expressions // All other expressions
_ => (), _ => (),
@ -1048,7 +1050,7 @@ fn parse_switch(
let (hash, range) = if let Some(expr) = expr { let (hash, range) = if let Some(expr) = expr {
let value = expr.get_literal_value().ok_or_else(|| { let value = expr.get_literal_value().ok_or_else(|| {
PERR::ExprExpected("a literal".to_string()).into_err(expr.position()) PERR::ExprExpected("a literal".to_string()).into_err(expr.start_position())
})?; })?;
let guard = value.read_lock::<ExclusiveRange>(); let guard = value.read_lock::<ExclusiveRange>();
@ -1058,14 +1060,14 @@ fn parse_switch(
} else if let Some(range) = value.read_lock::<InclusiveRange>() { } else if let Some(range) = value.read_lock::<InclusiveRange>() {
(None, Some((*range.start(), *range.end(), true))) (None, Some((*range.start(), *range.end(), true)))
} else if value.is::<INT>() && !ranges.is_empty() { } else if value.is::<INT>() && !ranges.is_empty() {
return Err(PERR::WrongSwitchIntegerCase.into_err(expr.position())); return Err(PERR::WrongSwitchIntegerCase.into_err(expr.start_position()));
} else { } else {
let hasher = &mut get_hasher(); let hasher = &mut get_hasher();
value.hash(hasher); value.hash(hasher);
let hash = hasher.finish(); let hash = hasher.finish();
if cases.contains_key(&hash) { if cases.contains_key(&hash) {
return Err(PERR::DuplicatedSwitchCase.into_err(expr.position())); return Err(PERR::DuplicatedSwitchCase.into_err(expr.start_position()));
} }
(Some(hash), None) (Some(hash), None)
} }
@ -1682,6 +1684,7 @@ fn parse_unary(
name: state.get_identifier("", "-"), name: state.get_identifier("", "-"),
hashes: FnCallHashes::from_native(calc_fn_hash("-", 1)), hashes: FnCallHashes::from_native(calc_fn_hash("-", 1)),
args, args,
pos,
..Default::default() ..Default::default()
} }
.into_fn_call_expr(pos)) .into_fn_call_expr(pos))
@ -1708,6 +1711,7 @@ fn parse_unary(
name: state.get_identifier("", "+"), name: state.get_identifier("", "+"),
hashes: FnCallHashes::from_native(calc_fn_hash("+", 1)), hashes: FnCallHashes::from_native(calc_fn_hash("+", 1)),
args, args,
pos,
..Default::default() ..Default::default()
} }
.into_fn_call_expr(pos)) .into_fn_call_expr(pos))
@ -1725,6 +1729,7 @@ fn parse_unary(
name: state.get_identifier("", "!"), name: state.get_identifier("", "!"),
hashes: FnCallHashes::from_native(calc_fn_hash("!", 1)), hashes: FnCallHashes::from_native(calc_fn_hash("!", 1)),
args, args,
pos,
..Default::default() ..Default::default()
} }
.into_fn_call_expr(pos)) .into_fn_call_expr(pos))
@ -1753,7 +1758,7 @@ fn make_assignment_stmt(
} }
Expr::Property(_, _) => None, Expr::Property(_, _) => None,
// Anything other than a property after dotting (e.g. a method call) is not an l-value // Anything other than a property after dotting (e.g. a method call) is not an l-value
ref e => Some(e.position()), ref e => Some(e.start_position()),
}, },
Expr::Index(x, term, _) | Expr::Dot(x, term, _) => match x.lhs { Expr::Index(x, term, _) | Expr::Dot(x, term, _) => match x.lhs {
Expr::Property(_, _) => unreachable!("unexpected Expr::Property in indexing"), Expr::Property(_, _) => unreachable!("unexpected Expr::Property in indexing"),
@ -1762,7 +1767,7 @@ fn make_assignment_stmt(
}, },
Expr::Property(_, _) if parent_is_dot => None, Expr::Property(_, _) if parent_is_dot => None,
Expr::Property(_, _) => unreachable!("unexpected Expr::Property in indexing"), Expr::Property(_, _) => unreachable!("unexpected Expr::Property in indexing"),
e if parent_is_dot => Some(e.position()), e if parent_is_dot => Some(e.start_position()),
_ => None, _ => None,
} }
} }
@ -1772,7 +1777,7 @@ fn make_assignment_stmt(
match lhs { match lhs {
// const_expr = rhs // const_expr = rhs
ref expr if expr.is_constant() => { ref expr if expr.is_constant() => {
Err(PERR::AssignmentToConstant("".into()).into_err(lhs.position())) Err(PERR::AssignmentToConstant("".into()).into_err(lhs.start_position()))
} }
// var (non-indexed) = rhs // var (non-indexed) = rhs
Expr::Variable(None, _, ref x) if x.0.is_none() => Ok(Stmt::Assignment( Expr::Variable(None, _, ref x) if x.0.is_none() => Ok(Stmt::Assignment(
@ -1814,10 +1819,8 @@ fn make_assignment_stmt(
op_pos, op_pos,
)), )),
// expr[???] = rhs, expr.??? = rhs // expr[???] = rhs, expr.??? = rhs
ref expr => { ref expr => Err(PERR::AssignmentToInvalidLHS("".to_string())
Err(PERR::AssignmentToInvalidLHS("".to_string()) .into_err(expr.start_position())),
.into_err(expr.position()))
}
} }
} }
Some(err_pos) => { Some(err_pos) => {
@ -1832,7 +1835,7 @@ fn make_assignment_stmt(
) )
.into_err(op_pos)), .into_err(op_pos)),
// expr = rhs // expr = rhs
_ => Err(PERR::AssignmentToInvalidLHS("".to_string()).into_err(lhs.position())), _ => Err(PERR::AssignmentToInvalidLHS("".to_string()).into_err(lhs.start_position())),
} }
} }
@ -1983,7 +1986,7 @@ fn make_dot_expr(
Ok(Expr::Dot(BinaryExpr { lhs, rhs }.into(), false, op_pos)) Ok(Expr::Dot(BinaryExpr { lhs, rhs }.into(), false, op_pos))
} }
// lhs.rhs // lhs.rhs
(_, rhs) => Err(PERR::PropertyExpected.into_err(rhs.position())), (_, rhs) => Err(PERR::PropertyExpected.into_err(rhs.start_position())),
} }
} }
@ -2065,6 +2068,7 @@ fn parse_binary_op(
let op_base = FnCallExpr { let op_base = FnCallExpr {
name: state.get_identifier("", op), name: state.get_identifier("", op),
hashes: FnCallHashes::from_native(hash), hashes: FnCallHashes::from_native(hash),
pos,
..Default::default() ..Default::default()
}; };
@ -2082,7 +2086,10 @@ fn parse_binary_op(
| Token::LessThan | Token::LessThan
| Token::LessThanEqualsTo | Token::LessThanEqualsTo
| Token::GreaterThan | Token::GreaterThan
| Token::GreaterThanEqualsTo => FnCallExpr { args, ..op_base }.into_fn_call_expr(pos), | Token::GreaterThanEqualsTo => {
let pos = args[0].start_position();
FnCallExpr { args, ..op_base }.into_fn_call_expr(pos)
}
Token::Or => { Token::Or => {
let rhs = args.pop().unwrap(); let rhs = args.pop().unwrap();
@ -2111,6 +2118,7 @@ fn parse_binary_op(
Token::In => { Token::In => {
// Swap the arguments // Swap the arguments
let current_lhs = args.remove(0); let current_lhs = args.remove(0);
let pos = current_lhs.start_position();
args.push(current_lhs); args.push(current_lhs);
args.shrink_to_fit(); args.shrink_to_fit();
@ -2132,6 +2140,7 @@ fn parse_binary_op(
.map_or(false, Option::is_some) => .map_or(false, Option::is_some) =>
{ {
let hash = calc_fn_hash(&s, 2); let hash = calc_fn_hash(&s, 2);
let pos = args[0].start_position();
FnCallExpr { FnCallExpr {
hashes: if is_valid_function_name(&s) { hashes: if is_valid_function_name(&s) {
@ -2145,7 +2154,10 @@ fn parse_binary_op(
.into_fn_call_expr(pos) .into_fn_call_expr(pos)
} }
_ => FnCallExpr { args, ..op_base }.into_fn_call_expr(pos), _ => {
let pos = args[0].start_position();
FnCallExpr { args, ..op_base }.into_fn_call_expr(pos)
}
}; };
} }
} }
@ -2734,13 +2746,10 @@ fn parse_block(
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
let orig_imports_len = state.imports.len(); let orig_imports_len = state.imports.len();
loop { let end_pos = loop {
// Terminated? // Terminated?
match input.peek().expect(NEVER_ENDS) { match input.peek().expect(NEVER_ENDS) {
(Token::RightBrace, _) => { (Token::RightBrace, _) => break eat_token(input, Token::RightBrace),
eat_token(input, Token::RightBrace);
break;
}
(Token::EOF, pos) => { (Token::EOF, pos) => {
return Err(PERR::MissingToken( return Err(PERR::MissingToken(
Token::RightBrace.into(), Token::RightBrace.into(),
@ -2767,10 +2776,7 @@ fn parse_block(
match input.peek().expect(NEVER_ENDS) { match input.peek().expect(NEVER_ENDS) {
// { ... stmt } // { ... stmt }
(Token::RightBrace, _) => { (Token::RightBrace, _) => break eat_token(input, Token::RightBrace),
eat_token(input, Token::RightBrace);
break;
}
// { ... stmt; // { ... stmt;
(Token::SemiColon, _) if need_semicolon => { (Token::SemiColon, _) if need_semicolon => {
eat_token(input, Token::SemiColon); eat_token(input, Token::SemiColon);
@ -2793,7 +2799,7 @@ fn parse_block(
.into_err(*pos)); .into_err(*pos));
} }
} }
} };
state.stack.truncate(state.entry_stack_len); state.stack.truncate(state.entry_stack_len);
state.entry_stack_len = prev_entry_stack_len; state.entry_stack_len = prev_entry_stack_len;
@ -2801,7 +2807,10 @@ fn parse_block(
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
state.imports.truncate(orig_imports_len); state.imports.truncate(orig_imports_len);
Ok(Stmt::Block(statements.into_boxed_slice(), settings.pos)) Ok(Stmt::Block(
statements.into_boxed_slice(),
(settings.pos, end_pos),
))
} }
/// Parse an expression as a statement. /// Parse an expression as a statement.
@ -3244,6 +3253,7 @@ fn make_curry_from_externals(
num_externals + 1, num_externals + 1,
)), )),
args, args,
pos,
..Default::default() ..Default::default()
} }
.into_fn_call_expr(pos); .into_fn_call_expr(pos);
@ -3253,7 +3263,7 @@ fn make_curry_from_externals(
let mut statements = StaticVec::with_capacity(externals.len() + 1); let mut statements = StaticVec::with_capacity(externals.len() + 1);
statements.extend(externals.into_iter().map(Stmt::Share)); statements.extend(externals.into_iter().map(Stmt::Share));
statements.push(Stmt::Expr(expr)); statements.push(Stmt::Expr(expr));
Expr::Stmt(crate::ast::StmtBlock::new(statements, pos).into()) Expr::Stmt(crate::ast::StmtBlock::new(statements, pos, Position::NONE).into())
} }
/// Parse an anonymous function definition. /// Parse an anonymous function definition.