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.
* `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
-----------------

View File

@ -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"))]

View File

@ -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

View File

@ -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)

View File

@ -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),

View File

@ -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),

View File

@ -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();

View File

@ -426,23 +426,26 @@ impl Engine {
};
#[cfg(feature = "debugging")]
if self.debugger.is_some() {
match global.debugger.status {
crate::eval::DebuggerStatus::FunctionExit(n) if n >= level => {
let scope = &mut &mut Scope::new();
let node = crate::ast::Stmt::Noop(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, &mut None, node, event, level,
) {
_result = Err(err);
}
{
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();
let event = match _result {
Ok(ref r) => crate::eval::DebuggerEvent::FunctionExitWithValue(r),
Err(ref err) => crate::eval::DebuggerEvent::FunctionExitWithError(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
@ -977,7 +980,7 @@ impl Engine {
result?
},
arg_expr.position(),
arg_expr.start_position(),
))
}
@ -1378,24 +1381,17 @@ 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");
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() {
Ok(Dynamic::UNIT)
} else {
let new_scope = &mut Scope::new();
let result = self.call_script_fn(
new_scope, global, state, lib, &mut None, fn_def, &mut args, true, pos, level,
);
let mut source = module.id_raw().clone();
mem::swap(&mut global.source, &mut source);
global.source = source;
let result = self.call_script_fn(
new_scope, global, state, lib, &mut None, fn_def, &mut args, true, pos,
level,
);
global.source = source;
result
}
result
}
Some(f) if f.is_plugin_fn() => {

View File

@ -182,22 +182,24 @@ 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 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)
{
_result = Err(err);
}
{
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),
};
match self.run_debugger_raw(scope, global, state, lib, this_ptr, node, event, level)
{
Ok(_) => (),
Err(err) => _result = Err(err),
}
_ => (),
}
// 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() => {
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());
}
},

View File

@ -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.