Make sure we keep the starting position of each statement (for future uses).

This commit is contained in:
Stephen Chung 2020-07-28 10:26:20 +08:00
parent b70fd35f4a
commit b63ff56e09
3 changed files with 162 additions and 112 deletions

View File

@ -228,11 +228,6 @@ impl<T: Into<Dynamic>> From<T> for Target<'_> {
/// [INTERNALS] A type that holds all the current states of the Engine. /// [INTERNALS] A type that holds all the current states of the Engine.
/// Exported under the `internals` feature only. /// Exported under the `internals` feature only.
/// ///
/// # Safety
///
/// This type uses some unsafe code, mainly for avoiding cloning of local variable names via
/// direct lifetime casting.
///
/// ## WARNING /// ## WARNING
/// ///
/// This type is volatile and may change. /// This type is volatile and may change.
@ -1495,6 +1490,12 @@ impl Engine {
} }
/// Evaluate a statement /// Evaluate a statement
///
///
/// # Safety
///
/// This method uses some unsafe code, mainly for avoiding cloning of local variable names via
/// direct lifetime casting.
pub(crate) fn eval_stmt( pub(crate) fn eval_stmt(
&self, &self,
scope: &mut Scope, scope: &mut Scope,
@ -1538,7 +1539,7 @@ impl Engine {
// If-else statement // If-else statement
Stmt::IfThenElse(x) => { Stmt::IfThenElse(x) => {
let (expr, if_block, else_block) = x.as_ref(); let (expr, if_block, else_block, _) = x.as_ref();
self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)? self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?
.as_bool() .as_bool()
@ -1556,7 +1557,7 @@ impl Engine {
// While loop // While loop
Stmt::While(x) => loop { Stmt::While(x) => loop {
let (expr, body) = x.as_ref(); let (expr, body, _) = x.as_ref();
match self match self
.eval_expr(scope, mods, state, lib, this_ptr, expr, level)? .eval_expr(scope, mods, state, lib, this_ptr, expr, level)?
@ -1582,8 +1583,8 @@ impl Engine {
}, },
// Loop statement // Loop statement
Stmt::Loop(body) => loop { Stmt::Loop(x) => loop {
match self.eval_stmt(scope, mods, state, lib, this_ptr, body, level) { match self.eval_stmt(scope, mods, state, lib, this_ptr, &x.0, level) {
Ok(_) => (), Ok(_) => (),
Err(err) => match *err { Err(err) => match *err {
EvalAltResult::ErrorLoopBreak(false, _) => (), EvalAltResult::ErrorLoopBreak(false, _) => (),
@ -1595,7 +1596,7 @@ impl Engine {
// For loop // For loop
Stmt::For(x) => { Stmt::For(x) => {
let (name, expr, stmt) = x.as_ref(); let (name, expr, stmt, _) = x.as_ref();
let iter_type = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?; let iter_type = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?;
let tid = iter_type.type_id(); let tid = iter_type.type_id();
@ -1641,16 +1642,9 @@ impl Engine {
// Return value // Return value
Stmt::ReturnWithVal(x) if x.1.is_some() && (x.0).0 == ReturnType::Return => { Stmt::ReturnWithVal(x) if x.1.is_some() && (x.0).0 == ReturnType::Return => {
let expr = x.1.as_ref().unwrap();
Err(Box::new(EvalAltResult::Return( Err(Box::new(EvalAltResult::Return(
self.eval_expr( self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?,
scope,
mods,
state,
lib,
this_ptr,
x.1.as_ref().unwrap(),
level,
)?,
(x.0).1, (x.0).1,
))) )))
} }
@ -1662,15 +1656,8 @@ impl Engine {
// Throw value // Throw value
Stmt::ReturnWithVal(x) if x.1.is_some() && (x.0).0 == ReturnType::Exception => { Stmt::ReturnWithVal(x) if x.1.is_some() && (x.0).0 == ReturnType::Exception => {
let val = self.eval_expr( let expr = x.1.as_ref().unwrap();
scope, let val = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?;
mods,
state,
lib,
this_ptr,
x.1.as_ref().unwrap(),
level,
)?;
Err(Box::new(EvalAltResult::ErrorRuntime( Err(Box::new(EvalAltResult::ErrorRuntime(
val.take_string().unwrap_or_else(|_| "".into()), val.take_string().unwrap_or_else(|_| "".into()),
(x.0).1, (x.0).1,
@ -1686,23 +1673,16 @@ impl Engine {
// Let statement // Let statement
Stmt::Let(x) if x.1.is_some() => { Stmt::Let(x) if x.1.is_some() => {
let ((var_name, _), expr) = x.as_ref(); let ((var_name, _), expr, _) = x.as_ref();
let val = self.eval_expr( let expr = expr.as_ref().unwrap();
scope, let val = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?;
mods,
state,
lib,
this_ptr,
expr.as_ref().unwrap(),
level,
)?;
let var_name = unsafe_cast_var_name_to_lifetime(var_name, &state); let var_name = unsafe_cast_var_name_to_lifetime(var_name, &state);
scope.push_dynamic_value(var_name, ScopeEntryType::Normal, val, false); scope.push_dynamic_value(var_name, ScopeEntryType::Normal, val, false);
Ok(Default::default()) Ok(Default::default())
} }
Stmt::Let(x) => { Stmt::Let(x) => {
let ((var_name, _), _) = x.as_ref(); let ((var_name, _), _, _) = x.as_ref();
let var_name = unsafe_cast_var_name_to_lifetime(var_name, &state); let var_name = unsafe_cast_var_name_to_lifetime(var_name, &state);
scope.push(var_name, ()); scope.push(var_name, ());
Ok(Default::default()) Ok(Default::default())
@ -1710,7 +1690,7 @@ impl Engine {
// Const statement // Const statement
Stmt::Const(x) if x.1.is_constant() => { Stmt::Const(x) if x.1.is_constant() => {
let ((var_name, _), expr) = x.as_ref(); let ((var_name, _), expr, _) = x.as_ref();
let val = self.eval_expr(scope, mods, state, lib, this_ptr, &expr, level)?; let val = self.eval_expr(scope, mods, state, lib, this_ptr, &expr, level)?;
let var_name = unsafe_cast_var_name_to_lifetime(var_name, &state); let var_name = unsafe_cast_var_name_to_lifetime(var_name, &state);
scope.push_dynamic_value(var_name, ScopeEntryType::Constant, val, true); scope.push_dynamic_value(var_name, ScopeEntryType::Constant, val, true);
@ -1723,7 +1703,7 @@ impl Engine {
// Import statement // Import statement
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
Stmt::Import(x) => { Stmt::Import(x) => {
let (expr, (name, _pos)) = x.as_ref(); let (expr, (name, _pos), _) = x.as_ref();
// Guard against too many modules // Guard against too many modules
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
@ -1756,8 +1736,8 @@ impl Engine {
// Export statement // Export statement
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
Stmt::Export(list) => { Stmt::Export(x) => {
for ((id, id_pos), rename) in list.iter() { for ((id, id_pos), rename) in x.0.iter() {
// Mark scope variables as public // Mark scope variables as public
if let Some(index) = scope.get_index(id).map(|(i, _)| i) { if let Some(index) = scope.get_index(id).map(|(i, _)| i) {
let alias = rename.as_ref().map(|(n, _)| n).unwrap_or_else(|| id); let alias = rename.as_ref().map(|(n, _)| n).unwrap_or_else(|| id);

View File

@ -185,6 +185,7 @@ fn optimize_stmt(stmt: Stmt, state: &mut State, preserve_result: bool) -> Stmt {
optimize_expr(expr, state), optimize_expr(expr, state),
optimize_stmt(x.1, state, true), optimize_stmt(x.1, state, true),
None, None,
x.3,
))), ))),
}, },
// if expr { if_block } else { else_block } // if expr { if_block } else { else_block }
@ -201,6 +202,7 @@ fn optimize_stmt(stmt: Stmt, state: &mut State, preserve_result: bool) -> Stmt {
Stmt::Noop(_) => None, // Noop -> no else block Stmt::Noop(_) => None, // Noop -> no else block
stmt => Some(stmt), stmt => Some(stmt),
}, },
x.3,
))), ))),
}, },
// while expr { block } // while expr { block }
@ -211,7 +213,7 @@ fn optimize_stmt(stmt: Stmt, state: &mut State, preserve_result: bool) -> Stmt {
Stmt::Noop(pos) Stmt::Noop(pos)
} }
// while true { block } -> loop { block } // while true { block } -> loop { block }
Expr::True(_) => Stmt::Loop(Box::new(optimize_stmt(x.1, state, false))), Expr::True(_) => Stmt::Loop(Box::new((optimize_stmt(x.1, state, false), x.2))),
// while expr { block } // while expr { block }
expr => match optimize_stmt(x.1, state, false) { expr => match optimize_stmt(x.1, state, false) {
// while expr { break; } -> { expr; } // while expr { break; } -> { expr; }
@ -226,11 +228,11 @@ fn optimize_stmt(stmt: Stmt, state: &mut State, preserve_result: bool) -> Stmt {
Stmt::Block(Box::new((statements, pos))) Stmt::Block(Box::new((statements, pos)))
} }
// while expr { block } // while expr { block }
stmt => Stmt::While(Box::new((optimize_expr(expr, state), stmt))), stmt => Stmt::While(Box::new((optimize_expr(expr, state), stmt, x.2))),
}, },
}, },
// loop { block } // loop { block }
Stmt::Loop(block) => match optimize_stmt(*block, state, false) { Stmt::Loop(x) => match optimize_stmt(x.0, state, false) {
// loop { break; } -> Noop // loop { break; } -> Noop
Stmt::Break(pos) => { Stmt::Break(pos) => {
// Only a single break statement // Only a single break statement
@ -238,23 +240,26 @@ fn optimize_stmt(stmt: Stmt, state: &mut State, preserve_result: bool) -> Stmt {
Stmt::Noop(pos) Stmt::Noop(pos)
} }
// loop { block } // loop { block }
stmt => Stmt::Loop(Box::new(stmt)), stmt => Stmt::Loop(Box::new((stmt, x.1))),
}, },
// for id in expr { block } // for id in expr { block }
Stmt::For(x) => Stmt::For(Box::new(( Stmt::For(x) => Stmt::For(Box::new((
x.0, x.0,
optimize_expr(x.1, state), optimize_expr(x.1, state),
optimize_stmt(x.2, state, false), optimize_stmt(x.2, state, false),
x.3,
))), ))),
// let id = expr; // let id = expr;
Stmt::Let(x) if x.1.is_some() => { Stmt::Let(x) if x.1.is_some() => Stmt::Let(Box::new((
Stmt::Let(Box::new((x.0, Some(optimize_expr(x.1.unwrap(), state))))) x.0,
} Some(optimize_expr(x.1.unwrap(), state)),
x.2,
))),
// let id; // let id;
stmt @ Stmt::Let(_) => stmt, stmt @ Stmt::Let(_) => stmt,
// import expr as id; // import expr as id;
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
Stmt::Import(x) => Stmt::Import(Box::new((optimize_expr(x.0, state), x.1))), Stmt::Import(x) => Stmt::Import(Box::new((optimize_expr(x.0, state), x.1, x.2))),
// { block } // { block }
Stmt::Block(x) => { Stmt::Block(x) => {
let orig_len = x.0.len(); // Original number of statements in the block, for change detection let orig_len = x.0.len(); // Original number of statements in the block, for change detection
@ -267,7 +272,7 @@ fn optimize_stmt(stmt: Stmt, state: &mut State, preserve_result: bool) -> Stmt {
.map(|stmt| match stmt { .map(|stmt| match stmt {
// Add constant into the state // Add constant into the state
Stmt::Const(v) => { Stmt::Const(v) => {
let ((name, pos), expr) = *v; let ((name, pos), expr, _) = *v;
state.push_constant(&name, expr); state.push_constant(&name, expr);
state.set_dirty(); state.set_dirty();
Stmt::Noop(pos) // No need to keep constants Stmt::Noop(pos) // No need to keep constants
@ -367,9 +372,11 @@ fn optimize_stmt(stmt: Stmt, state: &mut State, preserve_result: bool) -> Stmt {
// expr; // expr;
Stmt::Expr(expr) => Stmt::Expr(Box::new(optimize_expr(*expr, state))), Stmt::Expr(expr) => Stmt::Expr(Box::new(optimize_expr(*expr, state))),
// return expr; // return expr;
Stmt::ReturnWithVal(x) if x.1.is_some() => { Stmt::ReturnWithVal(x) if x.1.is_some() => Stmt::ReturnWithVal(Box::new((
Stmt::ReturnWithVal(Box::new((x.0, Some(optimize_expr(x.1.unwrap(), state))))) x.0,
} Some(optimize_expr(x.1.unwrap(), state)),
x.2,
))),
// All other statements - skip // All other statements - skip
stmt => stmt, stmt => stmt,
} }
@ -412,7 +419,7 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr {
state.set_dirty(); state.set_dirty();
let pos = m.1; let pos = m.1;
m.0.into_iter().find(|((name, _), _)| name.as_str() == prop.as_str()) m.0.into_iter().find(|((name, _), _)| name.as_str() == prop.as_str())
.map(|(_, expr)| expr.set_position(pos)) .map(|(_, mut expr)| { expr.set_position(pos); expr })
.unwrap_or_else(|| Expr::Unit(pos)) .unwrap_or_else(|| Expr::Unit(pos))
} }
// lhs.rhs // lhs.rhs
@ -429,7 +436,9 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr {
// Array literal where everything is pure - promote the indexed item. // Array literal where everything is pure - promote the indexed item.
// All other items can be thrown away. // All other items can be thrown away.
state.set_dirty(); state.set_dirty();
a.0.take(i.0 as usize).set_position(a.1) let mut expr = a.0.take(i.0 as usize);
expr.set_position(a.1);
expr
} }
// map[string] // map[string]
(Expr::Map(m), Expr::StringConstant(s)) if m.0.iter().all(|(_, x)| x.is_pure()) => { (Expr::Map(m), Expr::StringConstant(s)) if m.0.iter().all(|(_, x)| x.is_pure()) => {
@ -438,7 +447,7 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr {
state.set_dirty(); state.set_dirty();
let pos = m.1; let pos = m.1;
m.0.into_iter().find(|((name, _), _)| *name == s.0) m.0.into_iter().find(|((name, _), _)| *name == s.0)
.map(|(_, expr)| expr.set_position(pos)) .map(|(_, mut expr)| { expr.set_position(pos); expr })
.unwrap_or_else(|| Expr::Unit(pos)) .unwrap_or_else(|| Expr::Unit(pos))
} }
// string[int] // string[int]
@ -625,7 +634,9 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr {
state.set_dirty(); state.set_dirty();
// Replace constant with value // Replace constant with value
state.find_constant(&name).unwrap().clone().set_position(pos) let mut expr = state.find_constant(&name).unwrap().clone();
expr.set_position(pos);
expr
} }
// Custom syntax // Custom syntax
@ -687,7 +698,7 @@ fn optimize(
match &stmt { match &stmt {
Stmt::Const(v) => { Stmt::Const(v) => {
// Load constants // Load constants
let ((name, _), expr) = v.as_ref(); let ((name, _), expr, _) = v.as_ref();
state.push_constant(&name, expr.clone()); state.push_constant(&name, expr.clone());
stmt // Keep it in the global scope stmt // Keep it in the global scope
} }

View File

@ -26,7 +26,6 @@ use crate::stdlib::{
fmt, format, fmt, format,
hash::{Hash, Hasher}, hash::{Hash, Hasher},
iter::empty, iter::empty,
mem,
num::NonZeroUsize, num::NonZeroUsize,
ops::Add, ops::Add,
string::{String, ToString}, string::{String, ToString},
@ -511,33 +510,38 @@ pub enum Stmt {
/// No-op. /// No-op.
Noop(Position), Noop(Position),
/// if expr { stmt } else { stmt } /// if expr { stmt } else { stmt }
IfThenElse(Box<(Expr, Stmt, Option<Stmt>)>), IfThenElse(Box<(Expr, Stmt, Option<Stmt>, Position)>),
/// while expr { stmt } /// while expr { stmt }
While(Box<(Expr, Stmt)>), While(Box<(Expr, Stmt, Position)>),
/// loop { stmt } /// loop { stmt }
Loop(Box<Stmt>), Loop(Box<(Stmt, Position)>),
/// for id in expr { stmt } /// for id in expr { stmt }
For(Box<(String, Expr, Stmt)>), For(Box<(String, Expr, Stmt, Position)>),
/// let id = expr /// let id = expr
Let(Box<((String, Position), Option<Expr>)>), Let(Box<((String, Position), Option<Expr>, Position)>),
/// const id = expr /// const id = expr
Const(Box<((String, Position), Expr)>), Const(Box<((String, Position), Expr, Position)>),
/// { stmt; ... } /// { stmt; ... }
Block(Box<(StaticVec<Stmt>, Position)>), Block(Box<(StaticVec<Stmt>, Position)>),
/// { stmt } /// expr
Expr(Box<Expr>), Expr(Box<Expr>),
/// continue /// continue
Continue(Position), Continue(Position),
/// break /// break
Break(Position), Break(Position),
/// return/throw /// return/throw
ReturnWithVal(Box<((ReturnType, Position), Option<Expr>)>), ReturnWithVal(Box<((ReturnType, Position), Option<Expr>, Position)>),
/// import expr as module /// import expr as module
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
Import(Box<(Expr, (String, Position))>), Import(Box<(Expr, (String, Position), Position)>),
/// expr id as name, ... /// expr id as name, ...
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
Export(Box<StaticVec<((String, Position), Option<(String, Position)>)>>), Export(
Box<(
StaticVec<((String, Position), Option<(String, Position)>)>,
Position,
)>,
),
} }
impl Default for Stmt { impl Default for Stmt {
@ -555,19 +559,44 @@ impl Stmt {
Stmt::Const(x) => (x.0).1, Stmt::Const(x) => (x.0).1,
Stmt::ReturnWithVal(x) => (x.0).1, Stmt::ReturnWithVal(x) => (x.0).1,
Stmt::Block(x) => x.1, Stmt::Block(x) => x.1,
Stmt::IfThenElse(x) => x.0.position(), Stmt::IfThenElse(x) => x.3,
Stmt::Expr(x) => x.position(), Stmt::Expr(x) => x.position(),
Stmt::While(x) => x.1.position(), Stmt::While(x) => x.2,
Stmt::Loop(x) => x.position(), Stmt::Loop(x) => x.1,
Stmt::For(x) => x.2.position(), Stmt::For(x) => x.3,
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
Stmt::Import(x) => (x.1).1, Stmt::Import(x) => x.2,
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
Stmt::Export(x) => (x.get(0).0).1, Stmt::Export(x) => x.1,
} }
} }
/// Override the `Position` of this statement.
pub fn set_position(&mut self, new_pos: Position) -> &mut Self {
match self {
Stmt::Noop(pos) | Stmt::Continue(pos) | Stmt::Break(pos) => *pos = new_pos,
Stmt::Let(x) => (x.0).1 = new_pos,
Stmt::Const(x) => (x.0).1 = new_pos,
Stmt::ReturnWithVal(x) => (x.0).1 = new_pos,
Stmt::Block(x) => x.1 = new_pos,
Stmt::IfThenElse(x) => x.3 = new_pos,
Stmt::Expr(x) => {
x.set_position(new_pos);
}
Stmt::While(x) => x.2 = new_pos,
Stmt::Loop(x) => x.1 = new_pos,
Stmt::For(x) => x.3 = new_pos,
#[cfg(not(feature = "no_module"))]
Stmt::Import(x) => x.2 = new_pos,
#[cfg(not(feature = "no_module"))]
Stmt::Export(x) => x.1 = new_pos,
}
self
}
/// Is this statement self-terminated (i.e. no need for a semicolon terminator)? /// Is this statement self-terminated (i.e. no need for a semicolon terminator)?
pub fn is_self_terminated(&self) -> bool { pub fn is_self_terminated(&self) -> bool {
match self { match self {
@ -602,7 +631,7 @@ impl Stmt {
} }
Stmt::IfThenElse(x) => x.1.is_pure(), Stmt::IfThenElse(x) => x.1.is_pure(),
Stmt::While(x) => x.0.is_pure() && x.1.is_pure(), Stmt::While(x) => x.0.is_pure() && x.1.is_pure(),
Stmt::Loop(x) => x.is_pure(), Stmt::Loop(x) => x.0.is_pure(),
Stmt::For(x) => x.1.is_pure() && x.2.is_pure(), Stmt::For(x) => x.1.is_pure() && x.2.is_pure(),
Stmt::Let(_) | Stmt::Const(_) => false, Stmt::Let(_) | Stmt::Const(_) => false,
Stmt::Block(x) => x.0.iter().all(Stmt::is_pure), Stmt::Block(x) => x.0.iter().all(Stmt::is_pure),
@ -832,11 +861,10 @@ impl Expr {
} }
/// Override the `Position` of the expression. /// Override the `Position` of the expression.
pub(crate) fn set_position(mut self, new_pos: Position) -> Self { pub fn set_position(&mut self, new_pos: Position) -> &mut Self {
match &mut self { match self {
Self::Expr(ref mut x) => { Self::Expr(x) => {
let expr = mem::take(x); x.set_position(new_pos);
*x = Box::new(expr.set_position(new_pos));
} }
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
@ -2314,7 +2342,7 @@ fn ensure_not_statement_expr(input: &mut TokenStream, type_name: &str) -> Result
fn ensure_not_assignment(input: &mut TokenStream) -> Result<(), ParseError> { fn ensure_not_assignment(input: &mut TokenStream) -> Result<(), ParseError> {
match input.peek().unwrap() { match input.peek().unwrap() {
(Token::Equals, pos) => { (Token::Equals, pos) => {
return Err(PERR::BadInput("Possibly a typo of '=='?".to_string()).into_err(*pos)) Err(PERR::BadInput("Possibly a typo of '=='?".to_string()).into_err(*pos))
} }
(Token::PlusAssign, pos) (Token::PlusAssign, pos)
| (Token::MinusAssign, pos) | (Token::MinusAssign, pos)
@ -2326,12 +2354,10 @@ fn ensure_not_assignment(input: &mut TokenStream) -> Result<(), ParseError> {
| (Token::PowerOfAssign, pos) | (Token::PowerOfAssign, pos)
| (Token::AndAssign, pos) | (Token::AndAssign, pos)
| (Token::OrAssign, pos) | (Token::OrAssign, pos)
| (Token::XOrAssign, pos) => { | (Token::XOrAssign, pos) => Err(PERR::BadInput(
return Err(PERR::BadInput( "Expecting a boolean expression, not an assignment".to_string(),
"Expecting a boolean expression, not an assignment".to_string(), )
) .into_err(*pos)),
.into_err(*pos))
}
_ => Ok(()), _ => Ok(()),
} }
@ -2345,7 +2371,8 @@ fn parse_if(
mut settings: ParseSettings, mut settings: ParseSettings,
) -> Result<Stmt, ParseError> { ) -> Result<Stmt, ParseError> {
// if ... // if ...
settings.pos = eat_token(input, Token::If); let token_pos = eat_token(input, Token::If);
settings.pos = token_pos;
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
settings.ensure_level_within_max_limit(state.max_expr_depth)?; settings.ensure_level_within_max_limit(state.max_expr_depth)?;
@ -2369,7 +2396,9 @@ fn parse_if(
None None
}; };
Ok(Stmt::IfThenElse(Box::new((guard, if_body, else_body)))) Ok(Stmt::IfThenElse(Box::new((
guard, if_body, else_body, token_pos,
))))
} }
/// Parse a while loop. /// Parse a while loop.
@ -2380,7 +2409,8 @@ fn parse_while(
mut settings: ParseSettings, mut settings: ParseSettings,
) -> Result<Stmt, ParseError> { ) -> Result<Stmt, ParseError> {
// while ... // while ...
settings.pos = eat_token(input, Token::While); let token_pos = eat_token(input, Token::While);
settings.pos = token_pos;
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
settings.ensure_level_within_max_limit(state.max_expr_depth)?; settings.ensure_level_within_max_limit(state.max_expr_depth)?;
@ -2393,7 +2423,7 @@ fn parse_while(
settings.is_breakable = true; settings.is_breakable = true;
let body = parse_block(input, state, lib, settings.level_up())?; let body = parse_block(input, state, lib, settings.level_up())?;
Ok(Stmt::While(Box::new((guard, body)))) Ok(Stmt::While(Box::new((guard, body, token_pos))))
} }
/// Parse a loop statement. /// Parse a loop statement.
@ -2404,7 +2434,8 @@ fn parse_loop(
mut settings: ParseSettings, mut settings: ParseSettings,
) -> Result<Stmt, ParseError> { ) -> Result<Stmt, ParseError> {
// loop ... // loop ...
settings.pos = eat_token(input, Token::Loop); let token_pos = eat_token(input, Token::Loop);
settings.pos = token_pos;
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
settings.ensure_level_within_max_limit(state.max_expr_depth)?; settings.ensure_level_within_max_limit(state.max_expr_depth)?;
@ -2413,7 +2444,7 @@ fn parse_loop(
settings.is_breakable = true; settings.is_breakable = true;
let body = parse_block(input, state, lib, settings.level_up())?; let body = parse_block(input, state, lib, settings.level_up())?;
Ok(Stmt::Loop(Box::new(body))) Ok(Stmt::Loop(Box::new((body, token_pos))))
} }
/// Parse a for loop. /// Parse a for loop.
@ -2424,7 +2455,8 @@ fn parse_for(
mut settings: ParseSettings, mut settings: ParseSettings,
) -> Result<Stmt, ParseError> { ) -> Result<Stmt, ParseError> {
// for ... // for ...
settings.pos = eat_token(input, Token::For); let token_pos = eat_token(input, Token::For);
settings.pos = token_pos;
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
settings.ensure_level_within_max_limit(state.max_expr_depth)?; settings.ensure_level_within_max_limit(state.max_expr_depth)?;
@ -2467,7 +2499,7 @@ fn parse_for(
state.stack.truncate(prev_stack_len); state.stack.truncate(prev_stack_len);
Ok(Stmt::For(Box::new((name, expr, body)))) Ok(Stmt::For(Box::new((name, expr, body, token_pos))))
} }
/// Parse a variable definition statement. /// Parse a variable definition statement.
@ -2479,7 +2511,8 @@ fn parse_let(
mut settings: ParseSettings, mut settings: ParseSettings,
) -> Result<Stmt, ParseError> { ) -> Result<Stmt, ParseError> {
// let/const... (specified in `var_type`) // let/const... (specified in `var_type`)
settings.pos = input.next().unwrap().1; let token_pos = input.next().unwrap().1;
settings.pos = token_pos;
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
settings.ensure_level_within_max_limit(state.max_expr_depth)?; settings.ensure_level_within_max_limit(state.max_expr_depth)?;
@ -2503,12 +2536,16 @@ fn parse_let(
// let name = expr // let name = expr
ScopeEntryType::Normal => { ScopeEntryType::Normal => {
state.stack.push((name.clone(), ScopeEntryType::Normal)); state.stack.push((name.clone(), ScopeEntryType::Normal));
Ok(Stmt::Let(Box::new(((name, pos), Some(init_value))))) Ok(Stmt::Let(Box::new((
(name, pos),
Some(init_value),
token_pos,
))))
} }
// const name = { expr:constant } // const name = { expr:constant }
ScopeEntryType::Constant if init_value.is_constant() => { ScopeEntryType::Constant if init_value.is_constant() => {
state.stack.push((name.clone(), ScopeEntryType::Constant)); state.stack.push((name.clone(), ScopeEntryType::Constant));
Ok(Stmt::Const(Box::new(((name, pos), init_value)))) Ok(Stmt::Const(Box::new(((name, pos), init_value, token_pos))))
} }
// const name = expr: error // const name = expr: error
ScopeEntryType::Constant => { ScopeEntryType::Constant => {
@ -2520,11 +2557,15 @@ fn parse_let(
match var_type { match var_type {
ScopeEntryType::Normal => { ScopeEntryType::Normal => {
state.stack.push((name.clone(), ScopeEntryType::Normal)); state.stack.push((name.clone(), ScopeEntryType::Normal));
Ok(Stmt::Let(Box::new(((name, pos), None)))) Ok(Stmt::Let(Box::new(((name, pos), None, token_pos))))
} }
ScopeEntryType::Constant => { ScopeEntryType::Constant => {
state.stack.push((name.clone(), ScopeEntryType::Constant)); state.stack.push((name.clone(), ScopeEntryType::Constant));
Ok(Stmt::Const(Box::new(((name, pos), Expr::Unit(pos))))) Ok(Stmt::Const(Box::new((
(name, pos),
Expr::Unit(pos),
token_pos,
))))
} }
} }
} }
@ -2539,7 +2580,8 @@ fn parse_import(
mut settings: ParseSettings, mut settings: ParseSettings,
) -> Result<Stmt, ParseError> { ) -> Result<Stmt, ParseError> {
// import ... // import ...
settings.pos = eat_token(input, Token::Import); let token_pos = eat_token(input, Token::Import);
settings.pos = token_pos;
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
settings.ensure_level_within_max_limit(state.max_expr_depth)?; settings.ensure_level_within_max_limit(state.max_expr_depth)?;
@ -2569,7 +2611,12 @@ fn parse_import(
}; };
state.modules.push(name.clone()); state.modules.push(name.clone());
Ok(Stmt::Import(Box::new((expr, (name, settings.pos)))))
Ok(Stmt::Import(Box::new((
expr,
(name, settings.pos),
token_pos,
))))
} }
/// Parse an export statement. /// Parse an export statement.
@ -2580,7 +2627,8 @@ fn parse_export(
_lib: &mut FunctionsLib, _lib: &mut FunctionsLib,
mut settings: ParseSettings, mut settings: ParseSettings,
) -> Result<Stmt, ParseError> { ) -> Result<Stmt, ParseError> {
settings.pos = eat_token(input, Token::Export); let token_pos = eat_token(input, Token::Export);
settings.pos = token_pos;
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
settings.ensure_level_within_max_limit(_state.max_expr_depth)?; settings.ensure_level_within_max_limit(_state.max_expr_depth)?;
@ -2640,7 +2688,7 @@ fn parse_export(
}) })
.map_err(|(id2, pos)| PERR::DuplicatedExport(id2.to_string()).into_err(pos))?; .map_err(|(id2, pos)| PERR::DuplicatedExport(id2.to_string()).into_err(pos))?;
Ok(Stmt::Export(Box::new(exports))) Ok(Stmt::Export(Box::new((exports, token_pos))))
} }
/// Parse a statement block. /// Parse a statement block.
@ -2827,22 +2875,32 @@ fn parse_stmt(
Token::Continue | Token::Break => Err(PERR::LoopBreak.into_err(settings.pos)), Token::Continue | Token::Break => Err(PERR::LoopBreak.into_err(settings.pos)),
Token::Return | Token::Throw => { Token::Return | Token::Throw => {
let return_type = match input.next().unwrap() { let (return_type, token_pos) = input
(Token::Return, _) => ReturnType::Return, .next()
(Token::Throw, _) => ReturnType::Exception, .map(|(token, pos)| {
_ => unreachable!(), (
}; match token {
Token::Return => ReturnType::Return,
Token::Throw => ReturnType::Exception,
_ => unreachable!(),
},
pos,
)
})
.unwrap();
match input.peek().unwrap() { match input.peek().unwrap() {
// `return`/`throw` at <EOF> // `return`/`throw` at <EOF>
(Token::EOF, pos) => Ok(Some(Stmt::ReturnWithVal(Box::new(( (Token::EOF, pos) => Ok(Some(Stmt::ReturnWithVal(Box::new((
(return_type, *pos), (return_type, *pos),
None, None,
token_pos,
))))), ))))),
// `return;` or `throw;` // `return;` or `throw;`
(Token::SemiColon, _) => Ok(Some(Stmt::ReturnWithVal(Box::new(( (Token::SemiColon, _) => Ok(Some(Stmt::ReturnWithVal(Box::new((
(return_type, settings.pos), (return_type, settings.pos),
None, None,
token_pos,
))))), ))))),
// `return` or `throw` with expression // `return` or `throw` with expression
(_, _) => { (_, _) => {
@ -2852,6 +2910,7 @@ fn parse_stmt(
Ok(Some(Stmt::ReturnWithVal(Box::new(( Ok(Some(Stmt::ReturnWithVal(Box::new((
(return_type, pos), (return_type, pos),
Some(expr), Some(expr),
token_pos,
))))) )))))
} }
} }