Improve position display.
This commit is contained in:
parent
345a060672
commit
6c1c8bc538
@ -35,6 +35,9 @@ Enhancements
|
||||
* Debug display of `AST` is improved.
|
||||
* `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.
|
||||
* `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
|
||||
-----------------
|
||||
|
@ -91,7 +91,7 @@ impl AST {
|
||||
) -> Self {
|
||||
Self {
|
||||
source: Identifier::new_const(),
|
||||
body: StmtBlock::new(statements, Position::NONE),
|
||||
body: StmtBlock::new(statements, Position::NONE, Position::NONE),
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
lib: functions.into(),
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
|
@ -191,6 +191,8 @@ pub struct FnCallExpr {
|
||||
pub constants: StaticVec<Dynamic>,
|
||||
/// Does this function call capture the parent scope?
|
||||
pub capture_parent_scope: bool,
|
||||
/// [Position] of the function name.
|
||||
pub pos: Position,
|
||||
}
|
||||
|
||||
impl fmt::Debug for FnCallExpr {
|
||||
@ -207,6 +209,7 @@ impl fmt::Debug for FnCallExpr {
|
||||
if self.capture_parent_scope {
|
||||
ff.field("capture_parent_scope", &self.capture_parent_scope);
|
||||
}
|
||||
ff.field("pos", &self.pos);
|
||||
ff.finish()
|
||||
}
|
||||
}
|
||||
@ -437,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.position();
|
||||
let mut display_pos = self.start_position();
|
||||
|
||||
match self {
|
||||
Self::DynamicConstant(value, _) => write!(f, "{:?}", value),
|
||||
@ -629,6 +632,7 @@ impl Expr {
|
||||
args: once(Self::Stack(0, pos)).collect(),
|
||||
constants: once(f.fn_name().into()).collect(),
|
||||
capture_parent_scope: false,
|
||||
pos,
|
||||
}
|
||||
.into(),
|
||||
pos,
|
||||
@ -685,15 +689,30 @@ impl Expr {
|
||||
| Self::Map(_, pos)
|
||||
| Self::Variable(_, pos, _)
|
||||
| Self::Stack(_, pos)
|
||||
| Self::FnCall(_, pos)
|
||||
| Self::And(_, pos)
|
||||
| Self::Or(_, pos)
|
||||
| Self::Index(_, _, pos)
|
||||
| Self::Dot(_, _, pos)
|
||||
| Self::Custom(_, pos)
|
||||
| Self::InterpolatedString(_, 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.
|
||||
@ -722,7 +741,7 @@ impl Expr {
|
||||
| Self::InterpolatedString(_, 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
|
||||
|
@ -134,7 +134,7 @@ pub struct TryCatchBlock {
|
||||
/// _(internals)_ A scoped block of statements.
|
||||
/// Exported under the `internals` feature only.
|
||||
#[derive(Clone, Hash, Default)]
|
||||
pub struct StmtBlock(StaticVec<Stmt>, Position);
|
||||
pub struct StmtBlock(StaticVec<Stmt>, (Position, Position));
|
||||
|
||||
impl StmtBlock {
|
||||
/// A [`StmtBlock`] that does not exist.
|
||||
@ -142,16 +142,20 @@ impl StmtBlock {
|
||||
|
||||
/// Create a new [`StmtBlock`].
|
||||
#[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();
|
||||
statements.shrink_to_fit();
|
||||
Self(statements, pos)
|
||||
Self(statements, (start_pos, end_pos))
|
||||
}
|
||||
/// Create an empty [`StmtBlock`].
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn empty(pos: Position) -> Self {
|
||||
Self(StaticVec::new_const(), pos)
|
||||
Self(StaticVec::new_const(), (pos, pos))
|
||||
}
|
||||
/// Is this statements block empty?
|
||||
#[inline(always)]
|
||||
@ -183,16 +187,42 @@ impl StmtBlock {
|
||||
pub fn iter(&self) -> impl Iterator<Item = &Stmt> {
|
||||
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)]
|
||||
#[must_use]
|
||||
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
|
||||
}
|
||||
/// 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)]
|
||||
pub fn set_position(&mut self, pos: Position) {
|
||||
self.1 = pos;
|
||||
#[must_use]
|
||||
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 {
|
||||
f.write_str("Block")?;
|
||||
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 {
|
||||
match stmt {
|
||||
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();
|
||||
Self(vec![stmt].into(), pos)
|
||||
Self(vec![stmt].into(), (pos, Position::NONE))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -309,7 +344,7 @@ pub enum Stmt {
|
||||
/// function call forming one statement.
|
||||
FnCall(Box<FnCallExpr>, Position),
|
||||
/// `{` stmt`;` ... `}`
|
||||
Block(Box<[Stmt]>, Position),
|
||||
Block(Box<[Stmt]>, (Position, Position)),
|
||||
/// `try` `{` stmt; ... `}` `catch` `(` var `)` `{` stmt; ... `}`
|
||||
TryCatch(Box<TryCatchBlock>, Position),
|
||||
/// [expression][Expr]
|
||||
@ -377,7 +412,7 @@ impl Stmt {
|
||||
match self {
|
||||
Self::Noop(pos)
|
||||
| Self::BreakLoop(_, pos)
|
||||
| Self::Block(_, pos)
|
||||
| Self::Block(_, (pos, _))
|
||||
| Self::Assignment(_, pos)
|
||||
| Self::FnCall(_, pos)
|
||||
| Self::If(_, _, pos)
|
||||
@ -389,7 +424,7 @@ impl Stmt {
|
||||
| Self::Var(_, _, _, pos)
|
||||
| Self::TryCatch(_, pos) => *pos,
|
||||
|
||||
Self::Expr(x) => x.position(),
|
||||
Self::Expr(x) => x.start_position(),
|
||||
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
Self::Import(_, _, pos) => *pos,
|
||||
@ -405,7 +440,7 @@ impl Stmt {
|
||||
match self {
|
||||
Self::Noop(pos)
|
||||
| Self::BreakLoop(_, pos)
|
||||
| Self::Block(_, pos)
|
||||
| Self::Block(_, (pos, _))
|
||||
| Self::Assignment(_, pos)
|
||||
| Self::FnCall(_, pos)
|
||||
| Self::If(_, _, pos)
|
||||
|
@ -146,7 +146,7 @@ impl Engine {
|
||||
match chain_type {
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
ChainType::Indexing => {
|
||||
let pos = rhs.position();
|
||||
let pos = rhs.start_position();
|
||||
let root_pos = idx_val.position();
|
||||
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)?;
|
||||
|
||||
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 (try_setter, result) = {
|
||||
@ -629,7 +629,7 @@ impl Engine {
|
||||
}
|
||||
}
|
||||
// Syntax error
|
||||
_ => Err(ERR::ErrorDotExpr("".into(), rhs.position()).into()),
|
||||
_ => Err(ERR::ErrorDotExpr("".into(), rhs.start_position()).into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -691,7 +691,7 @@ impl Engine {
|
||||
expr => {
|
||||
let value = self.eval_expr(scope, global, state, lib, this_ptr, expr, level)?;
|
||||
let obj_ptr = &mut value.into();
|
||||
let root = ("", expr.position());
|
||||
let root = ("", expr.start_position());
|
||||
self.eval_dot_index_chain_helper(
|
||||
global, state, lib, this_ptr, obj_ptr, root, expr, rhs, term, idx_values,
|
||||
chain_type, level, new_val,
|
||||
@ -804,7 +804,10 @@ impl Engine {
|
||||
_ if _parent_chain_type == ChainType::Indexing => self
|
||||
.eval_expr(scope, global, state, lib, this_ptr, lhs, level)
|
||||
.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),
|
||||
};
|
||||
@ -828,7 +831,7 @@ impl Engine {
|
||||
_ if _parent_chain_type == ChainType::Indexing => idx_values.push(
|
||||
self.eval_expr(scope, global, state, lib, this_ptr, expr, level)
|
||||
.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),
|
||||
|
@ -148,7 +148,7 @@ impl Engine {
|
||||
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(None, pos, v) => (v.0.map(NonZeroUsize::get).unwrap_or(0), *pos),
|
||||
_ => unreachable!("Expr::Variable expected but gets {:?}", expr),
|
||||
@ -347,11 +347,11 @@ impl Engine {
|
||||
item,
|
||||
level,
|
||||
) {
|
||||
result = Err(err.fill_position(expr.position()));
|
||||
result = Err(err.fill_position(expr.start_position()));
|
||||
break;
|
||||
}
|
||||
|
||||
pos = expr.position();
|
||||
pos = expr.start_position();
|
||||
}
|
||||
|
||||
result.map(|_| concat)
|
||||
@ -504,7 +504,7 @@ impl Engine {
|
||||
|
||||
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),
|
||||
|
@ -267,7 +267,7 @@ impl Engine {
|
||||
rhs_val,
|
||||
level,
|
||||
)
|
||||
.map_err(|err| err.fill_position(rhs.position()))
|
||||
.map_err(|err| err.fill_position(rhs.start_position()))
|
||||
.map(|_| Dynamic::UNIT)
|
||||
} else {
|
||||
search_result.map(|_| Dynamic::UNIT)
|
||||
@ -283,7 +283,7 @@ impl Engine {
|
||||
.map(Dynamic::flatten);
|
||||
|
||||
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`
|
||||
match lhs {
|
||||
@ -686,7 +686,7 @@ impl Engine {
|
||||
|
||||
loop_result
|
||||
} else {
|
||||
Err(ERR::ErrorFor(expr.position()).into())
|
||||
Err(ERR::ErrorFor(expr.start_position()).into())
|
||||
}
|
||||
} else {
|
||||
iter_result
|
||||
@ -869,9 +869,10 @@ impl Engine {
|
||||
let path_result = self
|
||||
.eval_expr(scope, global, state, lib, this_ptr, &expr, level)
|
||||
.and_then(|v| {
|
||||
let typ = v.type_name();
|
||||
v.try_cast::<crate::ImmutableString>().ok_or_else(|| {
|
||||
self.make_type_mismatch_err::<crate::ImmutableString>(
|
||||
"",
|
||||
typ,
|
||||
expr.position(),
|
||||
)
|
||||
})
|
||||
@ -880,7 +881,7 @@ impl Engine {
|
||||
if let Ok(path) = path_result {
|
||||
use crate::ModuleResolver;
|
||||
|
||||
let path_pos = expr.position();
|
||||
let path_pos = expr.start_position();
|
||||
|
||||
let resolver = global.embedded_module_resolver.clone();
|
||||
|
||||
|
@ -426,9 +426,13 @@ impl Engine {
|
||||
};
|
||||
|
||||
#[cfg(feature = "debugging")]
|
||||
if self.debugger.is_some() {
|
||||
match global.debugger.status {
|
||||
crate::eval::DebuggerStatus::FunctionExit(n) if n >= level => {
|
||||
{
|
||||
let trigger = match global.debugger.status {
|
||||
crate::eval::DebuggerStatus::FunctionExit(n) => n >= level,
|
||||
crate::eval::DebuggerStatus::Next(_, true) => true,
|
||||
_ => false,
|
||||
};
|
||||
if trigger {
|
||||
let scope = &mut &mut Scope::new();
|
||||
let node = crate::ast::Stmt::Noop(pos);
|
||||
let node = (&node).into();
|
||||
@ -436,14 +440,13 @@ impl Engine {
|
||||
Ok(ref r) => crate::eval::DebuggerEvent::FunctionExitWithValue(r),
|
||||
Err(ref err) => crate::eval::DebuggerEvent::FunctionExitWithError(err),
|
||||
};
|
||||
if let Err(err) = self.run_debugger_raw(
|
||||
scope, global, state, lib, &mut None, node, event, level,
|
||||
) {
|
||||
_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
|
||||
global.debugger.rewind_call_stack(orig_call_stack_len);
|
||||
@ -977,7 +980,7 @@ impl Engine {
|
||||
|
||||
result?
|
||||
},
|
||||
arg_expr.position(),
|
||||
arg_expr.start_position(),
|
||||
))
|
||||
}
|
||||
|
||||
@ -1378,25 +1381,18 @@ impl Engine {
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
Some(f) if f.is_script() => {
|
||||
let fn_def = f.get_script_fn_def().expect("script-defined function");
|
||||
|
||||
if fn_def.body.is_empty() {
|
||||
Ok(Dynamic::UNIT)
|
||||
} else {
|
||||
let new_scope = &mut Scope::new();
|
||||
|
||||
let mut source = module.id_raw().clone();
|
||||
mem::swap(&mut global.source, &mut source);
|
||||
|
||||
let result = self.call_script_fn(
|
||||
new_scope, global, state, lib, &mut None, fn_def, &mut args, true, pos,
|
||||
level,
|
||||
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() => {
|
||||
let context = (self, fn_name, module.id(), &*global, lib, pos, level).into();
|
||||
|
@ -182,23 +182,25 @@ impl Engine {
|
||||
});
|
||||
|
||||
#[cfg(feature = "debugging")]
|
||||
if self.debugger.is_some() {
|
||||
match global.debugger.status {
|
||||
crate::eval::DebuggerStatus::FunctionExit(n) if n >= level => {
|
||||
let node = crate::ast::Stmt::Noop(pos);
|
||||
{
|
||||
let trigger = match global.debugger.status {
|
||||
crate::eval::DebuggerStatus::FunctionExit(n) => n >= level,
|
||||
crate::eval::DebuggerStatus::Next(_, true) => true,
|
||||
_ => false,
|
||||
};
|
||||
if trigger {
|
||||
let node = crate::ast::Stmt::Noop(fn_def.body.end_position().or_else(pos));
|
||||
let node = (&node).into();
|
||||
let event = match _result {
|
||||
Ok(ref r) => crate::eval::DebuggerEvent::FunctionExitWithValue(r),
|
||||
Err(ref err) => crate::eval::DebuggerEvent::FunctionExitWithError(err),
|
||||
};
|
||||
if let Err(err) = self
|
||||
.run_debugger_raw(scope, global, state, lib, this_ptr, node, event, level)
|
||||
match self.run_debugger_raw(scope, global, state, lib, this_ptr, node, event, level)
|
||||
{
|
||||
_result = Err(err);
|
||||
Ok(_) => (),
|
||||
Err(err) => _result = Err(err),
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
// Pop the call stack
|
||||
global.debugger.rewind_call_stack(orig_call_stack_len);
|
||||
|
@ -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() => {
|
||||
state.set_dirty();
|
||||
|
||||
let pos = condition.position();
|
||||
let pos = condition.start_position();
|
||||
let mut expr = mem::take(condition);
|
||||
optimize_expr(&mut expr, state, false);
|
||||
|
||||
*stmt = if preserve_result {
|
||||
// -> { 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 {
|
||||
// -> 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)
|
||||
{
|
||||
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
|
||||
@ -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)
|
||||
{
|
||||
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 }
|
||||
@ -531,11 +534,11 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
|
||||
mem::take(&mut block.statements),
|
||||
Stmt::Block(
|
||||
def_stmt.into_boxed_slice(),
|
||||
x.def_case.position().or_else(*pos),
|
||||
x.def_case.positions_or_else(*pos, Position::NONE),
|
||||
)
|
||||
.into(),
|
||||
)),
|
||||
match_expr.position(),
|
||||
match_expr.start_position(),
|
||||
);
|
||||
} else {
|
||||
// Promote the matched case
|
||||
@ -546,7 +549,8 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
|
||||
true,
|
||||
false,
|
||||
);
|
||||
*stmt = Stmt::Block(statements.into_boxed_slice(), block.statements.position());
|
||||
*stmt =
|
||||
Stmt::Block(statements.into_boxed_slice(), block.statements.positions());
|
||||
}
|
||||
|
||||
state.set_dirty();
|
||||
@ -586,11 +590,11 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
|
||||
mem::take(&mut block.statements),
|
||||
Stmt::Block(
|
||||
def_stmt.into_boxed_slice(),
|
||||
x.def_case.position().or_else(*pos),
|
||||
x.def_case.positions_or_else(*pos, Position::NONE),
|
||||
)
|
||||
.into(),
|
||||
)),
|
||||
match_expr.position(),
|
||||
match_expr.start_position(),
|
||||
);
|
||||
} else {
|
||||
// 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);
|
||||
*stmt = Stmt::Block(
|
||||
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);
|
||||
*stmt = Stmt::Block(
|
||||
def_stmt.into_boxed_slice(),
|
||||
x.def_case.position().or_else(*pos),
|
||||
x.def_case.positions_or_else(*pos, Position::NONE),
|
||||
);
|
||||
}
|
||||
// switch
|
||||
@ -704,7 +708,8 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
|
||||
if preserve_result {
|
||||
statements.push(Stmt::Noop(pos))
|
||||
}
|
||||
*stmt = Stmt::Block(statements.into_boxed_slice(), pos);
|
||||
*stmt =
|
||||
Stmt::Block(statements.into_boxed_slice(), (pos, Position::NONE));
|
||||
} else {
|
||||
*stmt = Stmt::Noop(pos);
|
||||
};
|
||||
@ -721,7 +726,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
|
||||
*stmt = Stmt::Block(
|
||||
optimize_stmt_block(mem::take(&mut **body), state, false, true, false)
|
||||
.into_boxed_slice(),
|
||||
body.position(),
|
||||
body.positions(),
|
||||
);
|
||||
}
|
||||
// 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() {
|
||||
[] => {
|
||||
state.set_dirty();
|
||||
*stmt = Stmt::Noop(*pos);
|
||||
*stmt = Stmt::Noop(pos.0);
|
||||
}
|
||||
// Only one statement which is not block-dependent - promote
|
||||
[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(
|
||||
optimize_stmt_block(mem::take(&mut *x.try_block), state, false, true, false)
|
||||
.into_boxed_slice(),
|
||||
x.try_block.position(),
|
||||
x.try_block.positions(),
|
||||
);
|
||||
}
|
||||
// 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() {
|
||||
state.set_dirty();
|
||||
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() {
|
||||
state.set_dirty();
|
||||
x.constants.push(value);
|
||||
*arg = Expr::Stack(x.constants.len()-1, arg.position());
|
||||
*arg = Expr::Stack(x.constants.len()-1, arg.start_position());
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -306,7 +306,7 @@ impl Expr {
|
||||
|
||||
Err(
|
||||
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.
|
||||
@ -326,7 +326,7 @@ impl Expr {
|
||||
|
||||
Err(
|
||||
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,
|
||||
hashes,
|
||||
args,
|
||||
pos: settings.pos,
|
||||
..Default::default()
|
||||
}
|
||||
.into_fn_call_expr(settings.pos));
|
||||
@ -585,6 +586,7 @@ fn parse_fn_call(
|
||||
namespace,
|
||||
hashes,
|
||||
args,
|
||||
pos: settings.pos,
|
||||
..Default::default()
|
||||
}
|
||||
.into_fn_call_expr(settings.pos));
|
||||
@ -652,7 +654,7 @@ fn parse_index_chain(
|
||||
return Err(PERR::MalformedIndexExpr(
|
||||
"Only arrays, object maps and strings can be indexed".into(),
|
||||
)
|
||||
.into_err(lhs.position()))
|
||||
.into_err(lhs.start_position()))
|
||||
}
|
||||
|
||||
Expr::CharConstant(_, _)
|
||||
@ -663,7 +665,7 @@ fn parse_index_chain(
|
||||
return Err(PERR::MalformedIndexExpr(
|
||||
"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(
|
||||
"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"))]
|
||||
@ -685,7 +687,7 @@ fn parse_index_chain(
|
||||
return Err(PERR::MalformedIndexExpr(
|
||||
"Only arrays, object maps and strings can be indexed".into(),
|
||||
)
|
||||
.into_err(lhs.position()))
|
||||
.into_err(lhs.start_position()))
|
||||
}
|
||||
|
||||
Expr::CharConstant(_, _)
|
||||
@ -696,7 +698,7 @@ fn parse_index_chain(
|
||||
return Err(PERR::MalformedIndexExpr(
|
||||
"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(
|
||||
"Array access expects integer index, not a float".into(),
|
||||
)
|
||||
.into_err(x.position()))
|
||||
.into_err(x.start_position()))
|
||||
}
|
||||
// lhs[char]
|
||||
x @ Expr::CharConstant(_, _) => {
|
||||
return Err(PERR::MalformedIndexExpr(
|
||||
"Array access expects integer index, not a character".into(),
|
||||
)
|
||||
.into_err(x.position()))
|
||||
.into_err(x.start_position()))
|
||||
}
|
||||
// lhs[()]
|
||||
x @ Expr::Unit(_) => {
|
||||
return Err(PERR::MalformedIndexExpr(
|
||||
"Array access expects integer index, not ()".into(),
|
||||
)
|
||||
.into_err(x.position()))
|
||||
.into_err(x.start_position()))
|
||||
}
|
||||
// lhs[??? && ???], lhs[??? || ???]
|
||||
x @ Expr::And(_, _) | x @ Expr::Or(_, _) => {
|
||||
return Err(PERR::MalformedIndexExpr(
|
||||
"Array access expects integer index, not a boolean".into(),
|
||||
)
|
||||
.into_err(x.position()))
|
||||
.into_err(x.start_position()))
|
||||
}
|
||||
// lhs[true], lhs[false]
|
||||
x @ Expr::BoolConstant(_, _) => {
|
||||
return Err(PERR::MalformedIndexExpr(
|
||||
"Array access expects integer index, not a boolean".into(),
|
||||
)
|
||||
.into_err(x.position()))
|
||||
.into_err(x.start_position()))
|
||||
}
|
||||
// All other expressions
|
||||
_ => (),
|
||||
@ -1048,7 +1050,7 @@ fn parse_switch(
|
||||
|
||||
let (hash, range) = if let Some(expr) = expr {
|
||||
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>();
|
||||
@ -1058,14 +1060,14 @@ fn parse_switch(
|
||||
} else if let Some(range) = value.read_lock::<InclusiveRange>() {
|
||||
(None, Some((*range.start(), *range.end(), true)))
|
||||
} 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 {
|
||||
let hasher = &mut get_hasher();
|
||||
value.hash(hasher);
|
||||
let hash = hasher.finish();
|
||||
|
||||
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)
|
||||
}
|
||||
@ -1682,6 +1684,7 @@ fn parse_unary(
|
||||
name: state.get_identifier("", "-"),
|
||||
hashes: FnCallHashes::from_native(calc_fn_hash("-", 1)),
|
||||
args,
|
||||
pos,
|
||||
..Default::default()
|
||||
}
|
||||
.into_fn_call_expr(pos))
|
||||
@ -1708,6 +1711,7 @@ fn parse_unary(
|
||||
name: state.get_identifier("", "+"),
|
||||
hashes: FnCallHashes::from_native(calc_fn_hash("+", 1)),
|
||||
args,
|
||||
pos,
|
||||
..Default::default()
|
||||
}
|
||||
.into_fn_call_expr(pos))
|
||||
@ -1725,6 +1729,7 @@ fn parse_unary(
|
||||
name: state.get_identifier("", "!"),
|
||||
hashes: FnCallHashes::from_native(calc_fn_hash("!", 1)),
|
||||
args,
|
||||
pos,
|
||||
..Default::default()
|
||||
}
|
||||
.into_fn_call_expr(pos))
|
||||
@ -1753,7 +1758,7 @@ fn make_assignment_stmt(
|
||||
}
|
||||
Expr::Property(_, _) => None,
|
||||
// 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::Property(_, _) => unreachable!("unexpected Expr::Property in indexing"),
|
||||
@ -1762,7 +1767,7 @@ fn make_assignment_stmt(
|
||||
},
|
||||
Expr::Property(_, _) if parent_is_dot => None,
|
||||
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,
|
||||
}
|
||||
}
|
||||
@ -1772,7 +1777,7 @@ fn make_assignment_stmt(
|
||||
match lhs {
|
||||
// const_expr = rhs
|
||||
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
|
||||
Expr::Variable(None, _, ref x) if x.0.is_none() => Ok(Stmt::Assignment(
|
||||
@ -1814,10 +1819,8 @@ fn make_assignment_stmt(
|
||||
op_pos,
|
||||
)),
|
||||
// expr[???] = rhs, expr.??? = rhs
|
||||
ref expr => {
|
||||
Err(PERR::AssignmentToInvalidLHS("".to_string())
|
||||
.into_err(expr.position()))
|
||||
}
|
||||
ref expr => Err(PERR::AssignmentToInvalidLHS("".to_string())
|
||||
.into_err(expr.start_position())),
|
||||
}
|
||||
}
|
||||
Some(err_pos) => {
|
||||
@ -1832,7 +1835,7 @@ fn make_assignment_stmt(
|
||||
)
|
||||
.into_err(op_pos)),
|
||||
// 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))
|
||||
}
|
||||
// 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 {
|
||||
name: state.get_identifier("", op),
|
||||
hashes: FnCallHashes::from_native(hash),
|
||||
pos,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
@ -2082,7 +2086,10 @@ fn parse_binary_op(
|
||||
| Token::LessThan
|
||||
| Token::LessThanEqualsTo
|
||||
| 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 => {
|
||||
let rhs = args.pop().unwrap();
|
||||
@ -2111,6 +2118,7 @@ fn parse_binary_op(
|
||||
Token::In => {
|
||||
// Swap the arguments
|
||||
let current_lhs = args.remove(0);
|
||||
let pos = current_lhs.start_position();
|
||||
args.push(current_lhs);
|
||||
args.shrink_to_fit();
|
||||
|
||||
@ -2132,6 +2140,7 @@ fn parse_binary_op(
|
||||
.map_or(false, Option::is_some) =>
|
||||
{
|
||||
let hash = calc_fn_hash(&s, 2);
|
||||
let pos = args[0].start_position();
|
||||
|
||||
FnCallExpr {
|
||||
hashes: if is_valid_function_name(&s) {
|
||||
@ -2145,7 +2154,10 @@ fn parse_binary_op(
|
||||
.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"))]
|
||||
let orig_imports_len = state.imports.len();
|
||||
|
||||
loop {
|
||||
let end_pos = loop {
|
||||
// Terminated?
|
||||
match input.peek().expect(NEVER_ENDS) {
|
||||
(Token::RightBrace, _) => {
|
||||
eat_token(input, Token::RightBrace);
|
||||
break;
|
||||
}
|
||||
(Token::RightBrace, _) => break eat_token(input, Token::RightBrace),
|
||||
(Token::EOF, pos) => {
|
||||
return Err(PERR::MissingToken(
|
||||
Token::RightBrace.into(),
|
||||
@ -2767,10 +2776,7 @@ fn parse_block(
|
||||
|
||||
match input.peek().expect(NEVER_ENDS) {
|
||||
// { ... stmt }
|
||||
(Token::RightBrace, _) => {
|
||||
eat_token(input, Token::RightBrace);
|
||||
break;
|
||||
}
|
||||
(Token::RightBrace, _) => break eat_token(input, Token::RightBrace),
|
||||
// { ... stmt;
|
||||
(Token::SemiColon, _) if need_semicolon => {
|
||||
eat_token(input, Token::SemiColon);
|
||||
@ -2793,7 +2799,7 @@ fn parse_block(
|
||||
.into_err(*pos));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
state.stack.truncate(state.entry_stack_len);
|
||||
state.entry_stack_len = prev_entry_stack_len;
|
||||
@ -2801,7 +2807,10 @@ fn parse_block(
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
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.
|
||||
@ -3244,6 +3253,7 @@ fn make_curry_from_externals(
|
||||
num_externals + 1,
|
||||
)),
|
||||
args,
|
||||
pos,
|
||||
..Default::default()
|
||||
}
|
||||
.into_fn_call_expr(pos);
|
||||
@ -3253,7 +3263,7 @@ fn make_curry_from_externals(
|
||||
let mut statements = StaticVec::with_capacity(externals.len() + 1);
|
||||
statements.extend(externals.into_iter().map(Stmt::Share));
|
||||
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.
|
||||
|
Loading…
Reference in New Issue
Block a user