Refine expression nesting level.
This commit is contained in:
parent
544b86adef
commit
a391f26920
@ -54,6 +54,7 @@ Enhancements
|
|||||||
* Line-style doc-comments are now merged into a single string to avoid creating many strings. Block-style doc-comments continue to be independent strings.
|
* Line-style doc-comments are now merged into a single string to avoid creating many strings. Block-style doc-comments continue to be independent strings.
|
||||||
* Block-style doc-comments are now "un-indented" for better formatting.
|
* Block-style doc-comments are now "un-indented" for better formatting.
|
||||||
* Doc-comments on plugin modules are now captured in the module's `doc` field.
|
* Doc-comments on plugin modules are now captured in the module's `doc` field.
|
||||||
|
* Expression nesting levels is refined such that it grows less excessively for common patterns.
|
||||||
|
|
||||||
|
|
||||||
Version 1.11.0
|
Version 1.11.0
|
||||||
|
@ -6,6 +6,7 @@ use crate::{Dynamic, Engine, EvalContext, Position, RhaiResultOf};
|
|||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
|
|
||||||
/// Information on a variable definition.
|
/// Information on a variable definition.
|
||||||
|
#[derive(Debug, Hash)]
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
pub struct VarDefInfo<'a> {
|
pub struct VarDefInfo<'a> {
|
||||||
/// Name of the variable to be defined.
|
/// Name of the variable to be defined.
|
||||||
|
223
src/parser.rs
223
src/parser.rs
@ -343,14 +343,15 @@ impl ParseSettings {
|
|||||||
/// Create a new `ParseSettings` with one higher expression level.
|
/// Create a new `ParseSettings` with one higher expression level.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn level_up(&self) -> ParseResult<Self> {
|
pub fn level_up(&self) -> ParseResult<Self> {
|
||||||
let level = self.level + 1;
|
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
if self.max_expr_depth > 0 && level > self.max_expr_depth {
|
if self.max_expr_depth > 0 && self.level >= self.max_expr_depth {
|
||||||
return Err(PERR::ExprTooDeep.into_err(self.pos));
|
return Err(PERR::ExprTooDeep.into_err(self.pos));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Self { level, ..*self })
|
Ok(Self {
|
||||||
|
level: self.level + 1,
|
||||||
|
..*self
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -537,50 +538,7 @@ fn parse_var_name(input: &mut TokenStream) -> ParseResult<(SmartString, Position
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a symbol.
|
|
||||||
#[cfg(not(feature = "no_custom_syntax"))]
|
|
||||||
fn parse_symbol(input: &mut TokenStream) -> ParseResult<(SmartString, Position)> {
|
|
||||||
match input.next().expect(NEVER_ENDS) {
|
|
||||||
// Symbol
|
|
||||||
(token, pos) if token.is_standard_symbol() => Ok((token.literal_syntax().into(), pos)),
|
|
||||||
// Reserved symbol
|
|
||||||
(Token::Reserved(s), pos) if !is_valid_identifier(s.as_str()) => Ok((*s, pos)),
|
|
||||||
// Bad symbol
|
|
||||||
(Token::LexError(err), pos) => Err(err.into_err(pos)),
|
|
||||||
// Not a symbol
|
|
||||||
(.., pos) => Err(PERR::MissingSymbol(String::new()).into_err(pos)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Engine {
|
impl Engine {
|
||||||
/// Parse `(` expr `)`
|
|
||||||
fn parse_paren_expr(
|
|
||||||
&self,
|
|
||||||
input: &mut TokenStream,
|
|
||||||
state: &mut ParseState,
|
|
||||||
lib: &mut FnLib,
|
|
||||||
settings: ParseSettings,
|
|
||||||
) -> ParseResult<Expr> {
|
|
||||||
// ( ...
|
|
||||||
let mut settings = settings;
|
|
||||||
settings.pos = eat_token(input, Token::LeftParen);
|
|
||||||
|
|
||||||
let expr = self.parse_expr(input, state, lib, settings.level_up()?)?;
|
|
||||||
|
|
||||||
match input.next().expect(NEVER_ENDS) {
|
|
||||||
// ( ... )
|
|
||||||
(Token::RightParen, ..) => Ok(expr),
|
|
||||||
// ( <error>
|
|
||||||
(Token::LexError(err), pos) => Err(err.into_err(pos)),
|
|
||||||
// ( ... ???
|
|
||||||
(.., pos) => Err(PERR::MissingToken(
|
|
||||||
Token::RightParen.into(),
|
|
||||||
"for a matching ( in this expression".into(),
|
|
||||||
)
|
|
||||||
.into_err(pos)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Parse a function call.
|
/// Parse a function call.
|
||||||
fn parse_fn_call(
|
fn parse_fn_call(
|
||||||
&self,
|
&self,
|
||||||
@ -974,10 +932,7 @@ impl Engine {
|
|||||||
)
|
)
|
||||||
.into_err(*pos))
|
.into_err(*pos))
|
||||||
}
|
}
|
||||||
_ => {
|
_ => array.push(self.parse_expr(input, state, lib, settings.level_up()?)?),
|
||||||
let expr = self.parse_expr(input, state, lib, settings.level_up()?)?;
|
|
||||||
array.push(expr);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
match input.peek().expect(NEVER_ENDS) {
|
match input.peek().expect(NEVER_ENDS) {
|
||||||
@ -1139,10 +1094,10 @@ impl Engine {
|
|||||||
settings: ParseSettings,
|
settings: ParseSettings,
|
||||||
) -> ParseResult<Stmt> {
|
) -> ParseResult<Stmt> {
|
||||||
// switch ...
|
// switch ...
|
||||||
let mut settings = settings;
|
let mut settings = settings.level_up()?;
|
||||||
settings.pos = eat_token(input, Token::Switch);
|
settings.pos = eat_token(input, Token::Switch);
|
||||||
|
|
||||||
let item = self.parse_expr(input, state, lib, settings.level_up()?)?;
|
let item = self.parse_expr(input, state, lib, settings)?;
|
||||||
|
|
||||||
match input.next().expect(NEVER_ENDS) {
|
match input.next().expect(NEVER_ENDS) {
|
||||||
(Token::LeftBrace, ..) => (),
|
(Token::LeftBrace, ..) => (),
|
||||||
@ -1201,7 +1156,7 @@ impl Engine {
|
|||||||
loop {
|
loop {
|
||||||
let filter = state.expr_filter;
|
let filter = state.expr_filter;
|
||||||
state.expr_filter = |t| t != &Token::Pipe;
|
state.expr_filter = |t| t != &Token::Pipe;
|
||||||
let expr = self.parse_expr(input, state, lib, settings.level_up()?);
|
let expr = self.parse_expr(input, state, lib, settings);
|
||||||
state.expr_filter = filter;
|
state.expr_filter = filter;
|
||||||
|
|
||||||
match expr {
|
match expr {
|
||||||
@ -1219,7 +1174,7 @@ impl Engine {
|
|||||||
let condition = if match_token(input, Token::If).0 {
|
let condition = if match_token(input, Token::If).0 {
|
||||||
ensure_not_statement_expr(input, "a boolean")?;
|
ensure_not_statement_expr(input, "a boolean")?;
|
||||||
let guard = self
|
let guard = self
|
||||||
.parse_expr(input, state, lib, settings.level_up()?)?
|
.parse_expr(input, state, lib, settings)?
|
||||||
.ensure_bool_expr()?;
|
.ensure_bool_expr()?;
|
||||||
ensure_not_assignment(input)?;
|
ensure_not_assignment(input)?;
|
||||||
guard
|
guard
|
||||||
@ -1244,16 +1199,13 @@ impl Engine {
|
|||||||
|
|
||||||
let (action_expr, need_comma) =
|
let (action_expr, need_comma) =
|
||||||
if !settings.has_flag(ParseSettingFlags::DISALLOW_STATEMENTS_IN_BLOCKS) {
|
if !settings.has_flag(ParseSettingFlags::DISALLOW_STATEMENTS_IN_BLOCKS) {
|
||||||
let stmt = self.parse_stmt(input, state, lib, settings.level_up()?)?;
|
let stmt = self.parse_stmt(input, state, lib, settings)?;
|
||||||
let need_comma = !stmt.is_self_terminated();
|
let need_comma = !stmt.is_self_terminated();
|
||||||
|
|
||||||
let stmt_block: StmtBlock = stmt.into();
|
let stmt_block: StmtBlock = stmt.into();
|
||||||
(Expr::Stmt(stmt_block.into()), need_comma)
|
(Expr::Stmt(stmt_block.into()), need_comma)
|
||||||
} else {
|
} else {
|
||||||
(
|
(self.parse_expr(input, state, lib, settings)?, true)
|
||||||
self.parse_expr(input, state, lib, settings.level_up()?)?,
|
|
||||||
true,
|
|
||||||
)
|
|
||||||
};
|
};
|
||||||
let has_condition = !matches!(condition, Expr::BoolConstant(true, ..));
|
let has_condition = !matches!(condition, Expr::BoolConstant(true, ..));
|
||||||
|
|
||||||
@ -1410,7 +1362,26 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ( - grouped expression
|
// ( - grouped expression
|
||||||
Token::LeftParen => self.parse_paren_expr(input, state, lib, settings.level_up()?)?,
|
Token::LeftParen => {
|
||||||
|
settings.pos = eat_token(input, Token::LeftParen);
|
||||||
|
|
||||||
|
let expr = self.parse_expr(input, state, lib, settings.level_up()?)?;
|
||||||
|
|
||||||
|
match input.next().expect(NEVER_ENDS) {
|
||||||
|
// ( ... )
|
||||||
|
(Token::RightParen, ..) => expr,
|
||||||
|
// ( <error>
|
||||||
|
(Token::LexError(err), pos) => return Err(err.into_err(pos)),
|
||||||
|
// ( ... ???
|
||||||
|
(.., pos) => {
|
||||||
|
return Err(PERR::MissingToken(
|
||||||
|
Token::RightParen.into(),
|
||||||
|
"for a matching ( in this expression".into(),
|
||||||
|
)
|
||||||
|
.into_err(pos))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If statement is allowed to act as expressions
|
// If statement is allowed to act as expressions
|
||||||
Token::If if settings.has_option(LangOptions::IF_EXPR) => Expr::Stmt(Box::new(
|
Token::If if settings.has_option(LangOptions::IF_EXPR) => Expr::Stmt(Box::new(
|
||||||
@ -1528,6 +1499,7 @@ impl Engine {
|
|||||||
// Interpolated string
|
// Interpolated string
|
||||||
Token::InterpolatedString(..) => {
|
Token::InterpolatedString(..) => {
|
||||||
let mut segments = StaticVec::<Expr>::new();
|
let mut segments = StaticVec::<Expr>::new();
|
||||||
|
let settings = settings.level_up()?;
|
||||||
|
|
||||||
match input.next().expect(NEVER_ENDS) {
|
match input.next().expect(NEVER_ENDS) {
|
||||||
(Token::InterpolatedString(s), ..) if s.is_empty() => (),
|
(Token::InterpolatedString(s), ..) if s.is_empty() => (),
|
||||||
@ -1540,7 +1512,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let expr = match self.parse_block(input, state, lib, settings.level_up()?)? {
|
let expr = match self.parse_block(input, state, lib, settings)? {
|
||||||
block @ Stmt::Block(..) => Expr::Stmt(Box::new(block.into())),
|
block @ Stmt::Block(..) => Expr::Stmt(Box::new(block.into())),
|
||||||
stmt => unreachable!("Stmt::Block expected but gets {:?}", stmt),
|
stmt => unreachable!("Stmt::Block expected but gets {:?}", stmt),
|
||||||
};
|
};
|
||||||
@ -1784,7 +1756,6 @@ impl Engine {
|
|||||||
|
|
||||||
let (.., ns, _, name) = *x;
|
let (.., ns, _, name) = *x;
|
||||||
settings.pos = pos;
|
settings.pos = pos;
|
||||||
let settings = settings.level_up()?;
|
|
||||||
self.parse_fn_call(input, state, lib, name, no_args, true, ns, settings)?
|
self.parse_fn_call(input, state, lib, name, no_args, true, ns, settings)?
|
||||||
}
|
}
|
||||||
// Function call
|
// Function call
|
||||||
@ -1792,7 +1763,6 @@ impl Engine {
|
|||||||
let (.., ns, _, name) = *x;
|
let (.., ns, _, name) = *x;
|
||||||
let no_args = t == Token::Unit;
|
let no_args = t == Token::Unit;
|
||||||
settings.pos = pos;
|
settings.pos = pos;
|
||||||
let settings = settings.level_up()?;
|
|
||||||
self.parse_fn_call(input, state, lib, name, no_args, false, ns, settings)?
|
self.parse_fn_call(input, state, lib, name, no_args, false, ns, settings)?
|
||||||
}
|
}
|
||||||
// module access
|
// module access
|
||||||
@ -2014,7 +1984,7 @@ impl Engine {
|
|||||||
// <EOF>
|
// <EOF>
|
||||||
Token::EOF => Err(PERR::UnexpectedEOF.into_err(settings.pos)),
|
Token::EOF => Err(PERR::UnexpectedEOF.into_err(settings.pos)),
|
||||||
// All other tokens
|
// All other tokens
|
||||||
_ => self.parse_primary(input, state, lib, false, settings.level_up()?),
|
_ => self.parse_primary(input, state, lib, false, settings),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2123,33 +2093,6 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse an operator-assignment expression (if any).
|
|
||||||
fn parse_op_assignment_stmt(
|
|
||||||
&self,
|
|
||||||
input: &mut TokenStream,
|
|
||||||
state: &mut ParseState,
|
|
||||||
lib: &mut FnLib,
|
|
||||||
lhs: Expr,
|
|
||||||
settings: ParseSettings,
|
|
||||||
) -> ParseResult<Stmt> {
|
|
||||||
let (op, pos) = match input.peek().expect(NEVER_ENDS) {
|
|
||||||
// var = ...
|
|
||||||
(Token::Equals, ..) => (NO_TOKEN, eat_token(input, Token::Equals)),
|
|
||||||
// var op= ...
|
|
||||||
(token, ..) if token.is_op_assignment() => {
|
|
||||||
input.next().map(|(op, pos)| (op, pos)).expect(NEVER_ENDS)
|
|
||||||
}
|
|
||||||
// Not op-assignment
|
|
||||||
_ => return Ok(Stmt::Expr(lhs.into())),
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut settings = settings;
|
|
||||||
settings.pos = pos;
|
|
||||||
|
|
||||||
let rhs = self.parse_expr(input, state, lib, settings.level_up()?)?;
|
|
||||||
Self::make_assignment_stmt(op, state, lhs, rhs, pos)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Make a dot expression.
|
/// Make a dot expression.
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
fn make_dot_expr(
|
fn make_dot_expr(
|
||||||
@ -2545,7 +2488,20 @@ impl Engine {
|
|||||||
inputs.push(Expr::Variable((None, ns, 0, name).into(), None, pos));
|
inputs.push(Expr::Variable((None, ns, 0, name).into(), None, pos));
|
||||||
}
|
}
|
||||||
CUSTOM_SYNTAX_MARKER_SYMBOL => {
|
CUSTOM_SYNTAX_MARKER_SYMBOL => {
|
||||||
let (symbol, pos) = parse_symbol(input)?;
|
let (symbol, pos) = match input.next().expect(NEVER_ENDS) {
|
||||||
|
// Standard symbol
|
||||||
|
(token, pos) if token.is_standard_symbol() => {
|
||||||
|
Ok((token.literal_syntax().into(), pos))
|
||||||
|
}
|
||||||
|
// Reserved symbol
|
||||||
|
(Token::Reserved(s), pos) if !is_valid_identifier(s.as_str()) => {
|
||||||
|
Ok((*s, pos))
|
||||||
|
}
|
||||||
|
// Bad symbol
|
||||||
|
(Token::LexError(err), pos) => Err(err.into_err(pos)),
|
||||||
|
// Not a symbol
|
||||||
|
(.., pos) => Err(PERR::MissingSymbol(String::new()).into_err(pos)),
|
||||||
|
}?;
|
||||||
let symbol = state.get_interned_string(symbol);
|
let symbol = state.get_interned_string(symbol);
|
||||||
segments.push(symbol.clone());
|
segments.push(symbol.clone());
|
||||||
tokens.push(state.get_interned_string(CUSTOM_SYNTAX_MARKER_SYMBOL));
|
tokens.push(state.get_interned_string(CUSTOM_SYNTAX_MARKER_SYMBOL));
|
||||||
@ -2677,8 +2633,9 @@ impl Engine {
|
|||||||
|
|
||||||
// Parse expression normally.
|
// Parse expression normally.
|
||||||
let precedence = Precedence::new(1);
|
let precedence = Precedence::new(1);
|
||||||
let lhs = self.parse_unary(input, state, lib, settings.level_up()?)?;
|
let settings = settings.level_up()?;
|
||||||
self.parse_binary_op(input, state, lib, precedence, lhs, settings.level_up()?)
|
let lhs = self.parse_unary(input, state, lib, settings)?;
|
||||||
|
self.parse_binary_op(input, state, lib, precedence, lhs, settings)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse an if statement.
|
/// Parse an if statement.
|
||||||
@ -2690,25 +2647,25 @@ impl Engine {
|
|||||||
settings: ParseSettings,
|
settings: ParseSettings,
|
||||||
) -> ParseResult<Stmt> {
|
) -> ParseResult<Stmt> {
|
||||||
// if ...
|
// if ...
|
||||||
let mut settings = settings;
|
let mut settings = settings.level_up()?;
|
||||||
settings.pos = eat_token(input, Token::If);
|
settings.pos = eat_token(input, Token::If);
|
||||||
|
|
||||||
// if guard { if_body }
|
// if guard { if_body }
|
||||||
ensure_not_statement_expr(input, "a boolean")?;
|
ensure_not_statement_expr(input, "a boolean")?;
|
||||||
let guard = self
|
let guard = self
|
||||||
.parse_expr(input, state, lib, settings.level_up()?)?
|
.parse_expr(input, state, lib, settings)?
|
||||||
.ensure_bool_expr()?;
|
.ensure_bool_expr()?;
|
||||||
ensure_not_assignment(input)?;
|
ensure_not_assignment(input)?;
|
||||||
let if_body = self.parse_block(input, state, lib, settings.level_up()?)?;
|
let if_body = self.parse_block(input, state, lib, settings)?;
|
||||||
|
|
||||||
// if guard { if_body } else ...
|
// if guard { if_body } else ...
|
||||||
let else_body = if match_token(input, Token::Else).0 {
|
let else_body = if match_token(input, Token::Else).0 {
|
||||||
if let (Token::If, ..) = input.peek().expect(NEVER_ENDS) {
|
if let (Token::If, ..) = input.peek().expect(NEVER_ENDS) {
|
||||||
// if guard { if_body } else if ...
|
// if guard { if_body } else if ...
|
||||||
self.parse_if(input, state, lib, settings.level_up()?)?
|
self.parse_if(input, state, lib, settings)?
|
||||||
} else {
|
} else {
|
||||||
// if guard { if_body } else { else-body }
|
// if guard { if_body } else { else-body }
|
||||||
self.parse_block(input, state, lib, settings.level_up()?)?
|
self.parse_block(input, state, lib, settings)?
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Stmt::Noop(Position::NONE)
|
Stmt::Noop(Position::NONE)
|
||||||
@ -2728,14 +2685,14 @@ impl Engine {
|
|||||||
lib: &mut FnLib,
|
lib: &mut FnLib,
|
||||||
settings: ParseSettings,
|
settings: ParseSettings,
|
||||||
) -> ParseResult<Stmt> {
|
) -> ParseResult<Stmt> {
|
||||||
let mut settings = settings;
|
let mut settings = settings.level_up()?;
|
||||||
|
|
||||||
// while|loops ...
|
// while|loops ...
|
||||||
let (guard, token_pos) = match input.next().expect(NEVER_ENDS) {
|
let (guard, token_pos) = match input.next().expect(NEVER_ENDS) {
|
||||||
(Token::While, pos) => {
|
(Token::While, pos) => {
|
||||||
ensure_not_statement_expr(input, "a boolean")?;
|
ensure_not_statement_expr(input, "a boolean")?;
|
||||||
let expr = self
|
let expr = self
|
||||||
.parse_expr(input, state, lib, settings.level_up()?)?
|
.parse_expr(input, state, lib, settings)?
|
||||||
.ensure_bool_expr()?;
|
.ensure_bool_expr()?;
|
||||||
ensure_not_assignment(input)?;
|
ensure_not_assignment(input)?;
|
||||||
(expr, pos)
|
(expr, pos)
|
||||||
@ -2746,7 +2703,7 @@ impl Engine {
|
|||||||
settings.pos = token_pos;
|
settings.pos = token_pos;
|
||||||
settings.flags |= ParseSettingFlags::BREAKABLE;
|
settings.flags |= ParseSettingFlags::BREAKABLE;
|
||||||
|
|
||||||
let body = self.parse_block(input, state, lib, settings.level_up()?)?;
|
let body = self.parse_block(input, state, lib, settings)?;
|
||||||
|
|
||||||
Ok(Stmt::While((guard, body.into()).into(), settings.pos))
|
Ok(Stmt::While((guard, body.into()).into(), settings.pos))
|
||||||
}
|
}
|
||||||
@ -2760,7 +2717,7 @@ impl Engine {
|
|||||||
settings: ParseSettings,
|
settings: ParseSettings,
|
||||||
) -> ParseResult<Stmt> {
|
) -> ParseResult<Stmt> {
|
||||||
// do ...
|
// do ...
|
||||||
let mut settings = settings;
|
let mut settings = settings.level_up()?;
|
||||||
let orig_breakable = settings.flags.contains(ParseSettingFlags::BREAKABLE);
|
let orig_breakable = settings.flags.contains(ParseSettingFlags::BREAKABLE);
|
||||||
settings.flags |= ParseSettingFlags::BREAKABLE;
|
settings.flags |= ParseSettingFlags::BREAKABLE;
|
||||||
|
|
||||||
@ -2768,7 +2725,7 @@ impl Engine {
|
|||||||
|
|
||||||
// do { body } [while|until] guard
|
// do { body } [while|until] guard
|
||||||
|
|
||||||
let body = self.parse_block(input, state, lib, settings.level_up()?)?;
|
let body = self.parse_block(input, state, lib, settings)?;
|
||||||
|
|
||||||
let negated = match input.next().expect(NEVER_ENDS) {
|
let negated = match input.next().expect(NEVER_ENDS) {
|
||||||
(Token::While, ..) => ASTFlags::NONE,
|
(Token::While, ..) => ASTFlags::NONE,
|
||||||
@ -2787,7 +2744,7 @@ impl Engine {
|
|||||||
|
|
||||||
ensure_not_statement_expr(input, "a boolean")?;
|
ensure_not_statement_expr(input, "a boolean")?;
|
||||||
let guard = self
|
let guard = self
|
||||||
.parse_expr(input, state, lib, settings.level_up()?)?
|
.parse_expr(input, state, lib, settings)?
|
||||||
.ensure_bool_expr()?;
|
.ensure_bool_expr()?;
|
||||||
ensure_not_assignment(input)?;
|
ensure_not_assignment(input)?;
|
||||||
|
|
||||||
@ -2803,7 +2760,7 @@ impl Engine {
|
|||||||
settings: ParseSettings,
|
settings: ParseSettings,
|
||||||
) -> ParseResult<Stmt> {
|
) -> ParseResult<Stmt> {
|
||||||
// for ...
|
// for ...
|
||||||
let mut settings = settings;
|
let mut settings = settings.level_up()?;
|
||||||
settings.pos = eat_token(input, Token::For);
|
settings.pos = eat_token(input, Token::For);
|
||||||
|
|
||||||
// for name ...
|
// for name ...
|
||||||
@ -2856,7 +2813,7 @@ impl Engine {
|
|||||||
// for name in expr { body }
|
// for name in expr { body }
|
||||||
ensure_not_statement_expr(input, "a boolean")?;
|
ensure_not_statement_expr(input, "a boolean")?;
|
||||||
let expr = self
|
let expr = self
|
||||||
.parse_expr(input, state, lib, settings.level_up()?)?
|
.parse_expr(input, state, lib, settings)?
|
||||||
.ensure_iterable()?;
|
.ensure_iterable()?;
|
||||||
|
|
||||||
let counter_var = Ident {
|
let counter_var = Ident {
|
||||||
@ -2883,7 +2840,7 @@ impl Engine {
|
|||||||
};
|
};
|
||||||
|
|
||||||
settings.flags |= ParseSettingFlags::BREAKABLE;
|
settings.flags |= ParseSettingFlags::BREAKABLE;
|
||||||
let body = self.parse_block(input, state, lib, settings.level_up()?)?;
|
let body = self.parse_block(input, state, lib, settings)?;
|
||||||
|
|
||||||
state.stack.as_deref_mut().unwrap().rewind(prev_stack_len);
|
state.stack.as_deref_mut().unwrap().rewind(prev_stack_len);
|
||||||
|
|
||||||
@ -3008,11 +2965,11 @@ impl Engine {
|
|||||||
settings: ParseSettings,
|
settings: ParseSettings,
|
||||||
) -> ParseResult<Stmt> {
|
) -> ParseResult<Stmt> {
|
||||||
// import ...
|
// import ...
|
||||||
let mut settings = settings;
|
let mut settings = settings.level_up()?;
|
||||||
settings.pos = eat_token(input, Token::Import);
|
settings.pos = eat_token(input, Token::Import);
|
||||||
|
|
||||||
// import expr ...
|
// import expr ...
|
||||||
let expr = self.parse_expr(input, state, lib, settings.level_up()?)?;
|
let expr = self.parse_expr(input, state, lib, settings)?;
|
||||||
|
|
||||||
let export = if match_token(input, Token::As).0 {
|
let export = if match_token(input, Token::As).0 {
|
||||||
// import expr as name ...
|
// import expr as name ...
|
||||||
@ -3052,6 +3009,7 @@ impl Engine {
|
|||||||
match input.peek().expect(NEVER_ENDS) {
|
match input.peek().expect(NEVER_ENDS) {
|
||||||
(Token::Let, pos) => {
|
(Token::Let, pos) => {
|
||||||
let pos = *pos;
|
let pos = *pos;
|
||||||
|
let settings = settings.level_up()?;
|
||||||
let mut stmt =
|
let mut stmt =
|
||||||
self.parse_let(input, state, lib, AccessMode::ReadWrite, true, settings)?;
|
self.parse_let(input, state, lib, AccessMode::ReadWrite, true, settings)?;
|
||||||
stmt.set_position(pos);
|
stmt.set_position(pos);
|
||||||
@ -3059,6 +3017,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
(Token::Const, pos) => {
|
(Token::Const, pos) => {
|
||||||
let pos = *pos;
|
let pos = *pos;
|
||||||
|
let settings = settings.level_up()?;
|
||||||
let mut stmt =
|
let mut stmt =
|
||||||
self.parse_let(input, state, lib, AccessMode::ReadOnly, true, settings)?;
|
self.parse_let(input, state, lib, AccessMode::ReadOnly, true, settings)?;
|
||||||
stmt.set_position(pos);
|
stmt.set_position(pos);
|
||||||
@ -3099,7 +3058,7 @@ impl Engine {
|
|||||||
settings: ParseSettings,
|
settings: ParseSettings,
|
||||||
) -> ParseResult<Stmt> {
|
) -> ParseResult<Stmt> {
|
||||||
// Must start with {
|
// Must start with {
|
||||||
let mut settings = settings;
|
let mut settings = settings.level_up()?;
|
||||||
settings.pos = match input.next().expect(NEVER_ENDS) {
|
settings.pos = match input.next().expect(NEVER_ENDS) {
|
||||||
(Token::LeftBrace, pos) => pos,
|
(Token::LeftBrace, pos) => pos,
|
||||||
(Token::LexError(err), pos) => return Err(err.into_err(pos)),
|
(Token::LexError(err), pos) => return Err(err.into_err(pos)),
|
||||||
@ -3115,7 +3074,7 @@ impl Engine {
|
|||||||
let mut statements = StaticVec::new_const();
|
let mut statements = StaticVec::new_const();
|
||||||
|
|
||||||
if settings.has_flag(ParseSettingFlags::DISALLOW_STATEMENTS_IN_BLOCKS) {
|
if settings.has_flag(ParseSettingFlags::DISALLOW_STATEMENTS_IN_BLOCKS) {
|
||||||
let stmt = self.parse_expr_stmt(input, state, lib, settings.level_up()?)?;
|
let stmt = self.parse_expr_stmt(input, state, lib, settings)?;
|
||||||
statements.push(stmt);
|
statements.push(stmt);
|
||||||
|
|
||||||
// Must end with }
|
// Must end with }
|
||||||
@ -3153,7 +3112,7 @@ impl Engine {
|
|||||||
// Parse statements inside the block
|
// Parse statements inside the block
|
||||||
settings.flags &= !ParseSettingFlags::GLOBAL_LEVEL;
|
settings.flags &= !ParseSettingFlags::GLOBAL_LEVEL;
|
||||||
|
|
||||||
let stmt = self.parse_stmt(input, state, lib, settings.level_up()?)?;
|
let stmt = self.parse_stmt(input, state, lib, settings)?;
|
||||||
|
|
||||||
if stmt.is_noop() {
|
if stmt.is_noop() {
|
||||||
continue;
|
continue;
|
||||||
@ -3215,9 +3174,24 @@ impl Engine {
|
|||||||
let mut settings = settings;
|
let mut settings = settings;
|
||||||
settings.pos = input.peek().expect(NEVER_ENDS).1;
|
settings.pos = input.peek().expect(NEVER_ENDS).1;
|
||||||
|
|
||||||
let expr = self.parse_expr(input, state, lib, settings.level_up()?)?;
|
let expr = self.parse_expr(input, state, lib, settings)?;
|
||||||
let stmt = self.parse_op_assignment_stmt(input, state, lib, expr, settings.level_up()?)?;
|
|
||||||
Ok(stmt)
|
let (op, pos) = match input.peek().expect(NEVER_ENDS) {
|
||||||
|
// var = ...
|
||||||
|
(Token::Equals, ..) => (NO_TOKEN, eat_token(input, Token::Equals)),
|
||||||
|
// var op= ...
|
||||||
|
(token, ..) if token.is_op_assignment() => {
|
||||||
|
input.next().map(|(op, pos)| (op, pos)).expect(NEVER_ENDS)
|
||||||
|
}
|
||||||
|
// Not op-assignment
|
||||||
|
_ => return Ok(Stmt::Expr(expr.into())),
|
||||||
|
};
|
||||||
|
|
||||||
|
settings.pos = pos;
|
||||||
|
|
||||||
|
let rhs = self.parse_expr(input, state, lib, settings)?;
|
||||||
|
|
||||||
|
Self::make_assignment_stmt(op, state, expr, rhs, pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a single statement.
|
/// Parse a single statement.
|
||||||
@ -3292,6 +3266,7 @@ impl Engine {
|
|||||||
(Token::EOF, pos) => return Ok(Stmt::Noop(*pos)),
|
(Token::EOF, pos) => return Ok(Stmt::Noop(*pos)),
|
||||||
(x, pos) => (x, *pos),
|
(x, pos) => (x, *pos),
|
||||||
};
|
};
|
||||||
|
|
||||||
settings.pos = token_pos;
|
settings.pos = token_pos;
|
||||||
|
|
||||||
match token {
|
match token {
|
||||||
@ -3503,11 +3478,11 @@ impl Engine {
|
|||||||
settings: ParseSettings,
|
settings: ParseSettings,
|
||||||
) -> ParseResult<Stmt> {
|
) -> ParseResult<Stmt> {
|
||||||
// try ...
|
// try ...
|
||||||
let mut settings = settings;
|
let mut settings = settings.level_up()?;
|
||||||
settings.pos = eat_token(input, Token::Try);
|
settings.pos = eat_token(input, Token::Try);
|
||||||
|
|
||||||
// try { try_block }
|
// try { try_block }
|
||||||
let try_block = self.parse_block(input, state, lib, settings.level_up()?)?;
|
let try_block = self.parse_block(input, state, lib, settings)?;
|
||||||
|
|
||||||
// try { try_block } catch
|
// try { try_block } catch
|
||||||
let (matched, catch_pos) = match_token(input, Token::Catch);
|
let (matched, catch_pos) = match_token(input, Token::Catch);
|
||||||
@ -3546,7 +3521,7 @@ impl Engine {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// try { try_block } catch ( var ) { catch_block }
|
// try { try_block } catch ( var ) { catch_block }
|
||||||
let catch_block = self.parse_block(input, state, lib, settings.level_up()?)?;
|
let catch_block = self.parse_block(input, state, lib, settings)?;
|
||||||
|
|
||||||
if !catch_var.is_empty() {
|
if !catch_var.is_empty() {
|
||||||
// Remove the error variable from the stack
|
// Remove the error variable from the stack
|
||||||
@ -3575,7 +3550,7 @@ impl Engine {
|
|||||||
settings: ParseSettings,
|
settings: ParseSettings,
|
||||||
#[cfg(feature = "metadata")] comments: impl IntoIterator<Item = Identifier>,
|
#[cfg(feature = "metadata")] comments: impl IntoIterator<Item = Identifier>,
|
||||||
) -> ParseResult<ScriptFnDef> {
|
) -> ParseResult<ScriptFnDef> {
|
||||||
let settings = settings;
|
let settings = settings.level_up()?;
|
||||||
|
|
||||||
let (token, pos) = input.next().expect(NEVER_ENDS);
|
let (token, pos) = input.next().expect(NEVER_ENDS);
|
||||||
|
|
||||||
@ -3642,7 +3617,7 @@ impl Engine {
|
|||||||
|
|
||||||
// Parse function body
|
// Parse function body
|
||||||
let body = match input.peek().expect(NEVER_ENDS) {
|
let body = match input.peek().expect(NEVER_ENDS) {
|
||||||
(Token::LeftBrace, ..) => self.parse_block(input, state, lib, settings.level_up()?)?,
|
(Token::LeftBrace, ..) => self.parse_block(input, state, lib, settings)?,
|
||||||
(.., pos) => return Err(PERR::FnMissingBody(name.into()).into_err(*pos)),
|
(.., pos) => return Err(PERR::FnMissingBody(name.into()).into_err(*pos)),
|
||||||
}
|
}
|
||||||
.into();
|
.into();
|
||||||
@ -3734,7 +3709,7 @@ impl Engine {
|
|||||||
lib: &mut FnLib,
|
lib: &mut FnLib,
|
||||||
settings: ParseSettings,
|
settings: ParseSettings,
|
||||||
) -> ParseResult<(Expr, ScriptFnDef)> {
|
) -> ParseResult<(Expr, ScriptFnDef)> {
|
||||||
let settings = settings;
|
let settings = settings.level_up()?;
|
||||||
let mut params_list = StaticVec::<ImmutableString>::new_const();
|
let mut params_list = StaticVec::<ImmutableString>::new_const();
|
||||||
|
|
||||||
if input.next().expect(NEVER_ENDS).0 != Token::Or && !match_token(input, Token::Pipe).0 {
|
if input.next().expect(NEVER_ENDS).0 != Token::Or && !match_token(input, Token::Pipe).0 {
|
||||||
@ -3781,7 +3756,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Parse function body
|
// Parse function body
|
||||||
let body = self.parse_stmt(input, state, lib, settings.level_up()?)?;
|
let body = self.parse_stmt(input, state, lib, settings)?;
|
||||||
|
|
||||||
// External variables may need to be processed in a consistent order,
|
// External variables may need to be processed in a consistent order,
|
||||||
// so extract them into a list.
|
// so extract them into a list.
|
||||||
|
@ -55,7 +55,8 @@ fn test_stack_overflow_parsing() -> Result<(), Box<EvalAltResult>> {
|
|||||||
"
|
"
|
||||||
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0 +
|
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0 +
|
||||||
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0 +
|
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0 +
|
||||||
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0
|
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0 +
|
||||||
|
1
|
||||||
",
|
",
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
@ -67,7 +68,7 @@ fn test_stack_overflow_parsing() -> Result<(), Box<EvalAltResult>> {
|
|||||||
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0 +
|
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0 +
|
||||||
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0 +
|
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0 +
|
||||||
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0 +
|
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0 +
|
||||||
1
|
1 + 2
|
||||||
"
|
"
|
||||||
)
|
)
|
||||||
.expect_err("should error")
|
.expect_err("should error")
|
||||||
@ -92,7 +93,7 @@ fn test_stack_overflow_parsing() -> Result<(), Box<EvalAltResult>> {
|
|||||||
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0 +
|
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0 +
|
||||||
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0 +
|
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0 +
|
||||||
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0 +
|
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0 +
|
||||||
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8
|
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9
|
||||||
",
|
",
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
@ -109,7 +110,7 @@ fn test_stack_overflow_parsing() -> Result<(), Box<EvalAltResult>> {
|
|||||||
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0 +
|
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0 +
|
||||||
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0 +
|
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0 +
|
||||||
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0 +
|
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0 +
|
||||||
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9
|
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0
|
||||||
"
|
"
|
||||||
)
|
)
|
||||||
.expect_err("should error")
|
.expect_err("should error")
|
||||||
|
Loading…
Reference in New Issue
Block a user