Merge branch 'master' into plugins

This commit is contained in:
Stephen Chung 2020-07-06 23:14:44 +08:00
commit b71d9acdc2
2 changed files with 156 additions and 109 deletions

View File

@ -55,7 +55,7 @@ pub struct FnPtr(ImmutableString);
impl FnPtr { impl FnPtr {
/// Create a new function pointer. /// Create a new function pointer.
pub(crate) fn new<S: Into<ImmutableString>>(name: S) -> Self { pub(crate) fn new_unchecked<S: Into<ImmutableString>>(name: S) -> Self {
Self(name.into()) Self(name.into())
} }
/// Get the name of the function. /// Get the name of the function.

View File

@ -47,6 +47,8 @@ type PERR = ParseErrorType;
pub use crate::utils::ImmutableString; pub use crate::utils::ImmutableString;
type FunctionsLib = HashMap<u64, ScriptFnDef, StraightHasherBuilder>;
/// Compiled AST (abstract syntax tree) of a Rhai script. /// Compiled AST (abstract syntax tree) of a Rhai script.
/// ///
/// Currently, `AST` is neither `Send` nor `Sync`. Turn on the `sync` feature to make it `Send + Sync`. /// Currently, `AST` is neither `Send` nor `Sync`. Turn on the `sync` feature to make it `Send + Sync`.
@ -381,14 +383,17 @@ struct ParseState<'e> {
modules: Vec<String>, modules: Vec<String>,
/// Maximum levels of expression nesting. /// Maximum levels of expression nesting.
max_expr_depth: usize, max_expr_depth: usize,
/// Maximum levels of expression nesting in functions.
max_function_expr_depth: usize,
} }
impl<'e> ParseState<'e> { impl<'e> ParseState<'e> {
/// Create a new `ParseState`. /// Create a new `ParseState`.
pub fn new(engine: &'e Engine, max_expr_depth: usize) -> Self { pub fn new(engine: &'e Engine, max_expr_depth: usize, max_function_expr_depth: usize) -> Self {
Self { Self {
engine, engine,
max_expr_depth, max_expr_depth,
max_function_expr_depth,
stack: Default::default(), stack: Default::default(),
modules: Default::default(), modules: Default::default(),
} }
@ -428,6 +433,8 @@ struct ParseSettings {
is_global: bool, is_global: bool,
/// Is the current position inside a loop? /// Is the current position inside a loop?
is_breakable: bool, is_breakable: bool,
/// Is anonymous function allowed?
allow_anonymous_fn: bool,
/// Is if-expression allowed? /// Is if-expression allowed?
allow_if_expr: bool, allow_if_expr: bool,
/// Is statement-expression allowed? /// Is statement-expression allowed?
@ -901,6 +908,7 @@ fn match_token(input: &mut TokenStream, token: Token) -> Result<bool, ParseError
fn parse_paren_expr( fn parse_paren_expr(
input: &mut TokenStream, input: &mut TokenStream,
state: &mut ParseState, state: &mut ParseState,
lib: &mut FunctionsLib,
settings: ParseSettings, settings: ParseSettings,
) -> Result<Expr, ParseError> { ) -> Result<Expr, ParseError> {
settings.ensure_level_within_max_limit(state.max_expr_depth)?; settings.ensure_level_within_max_limit(state.max_expr_depth)?;
@ -909,7 +917,7 @@ fn parse_paren_expr(
return Ok(Expr::Unit(settings.pos)); return Ok(Expr::Unit(settings.pos));
} }
let expr = parse_expr(input, state, settings.level_up())?; let expr = parse_expr(input, state, lib, settings.level_up())?;
match input.next().unwrap() { match input.next().unwrap() {
// ( xxx ) // ( xxx )
@ -929,6 +937,7 @@ fn parse_paren_expr(
fn parse_call_expr( fn parse_call_expr(
input: &mut TokenStream, input: &mut TokenStream,
state: &mut ParseState, state: &mut ParseState,
lib: &mut FunctionsLib,
id: String, id: String,
mut modules: Option<Box<ModuleRef>>, mut modules: Option<Box<ModuleRef>>,
settings: ParseSettings, settings: ParseSettings,
@ -987,7 +996,7 @@ fn parse_call_expr(
match input.peek().unwrap() { match input.peek().unwrap() {
// id(...args, ) - handle trailing comma // id(...args, ) - handle trailing comma
(Token::RightParen, _) => (), (Token::RightParen, _) => (),
_ => args.push(parse_expr(input, state, settings)?), _ => args.push(parse_expr(input, state, lib, settings)?),
} }
match input.peek().unwrap() { match input.peek().unwrap() {
@ -1050,12 +1059,13 @@ fn parse_call_expr(
fn parse_index_chain( fn parse_index_chain(
input: &mut TokenStream, input: &mut TokenStream,
state: &mut ParseState, state: &mut ParseState,
lib: &mut FunctionsLib,
lhs: Expr, lhs: Expr,
mut settings: ParseSettings, mut settings: ParseSettings,
) -> Result<Expr, ParseError> { ) -> Result<Expr, ParseError> {
settings.ensure_level_within_max_limit(state.max_expr_depth)?; settings.ensure_level_within_max_limit(state.max_expr_depth)?;
let idx_expr = parse_expr(input, state, settings.level_up())?; let idx_expr = parse_expr(input, state, lib, settings.level_up())?;
// Check type of indexing - must be integer or string // Check type of indexing - must be integer or string
match &idx_expr { match &idx_expr {
@ -1197,7 +1207,8 @@ fn parse_index_chain(
let prev_pos = settings.pos; let prev_pos = settings.pos;
settings.pos = eat_token(input, Token::LeftBracket); settings.pos = eat_token(input, Token::LeftBracket);
// Recursively parse the indexing chain, right-binding each // Recursively parse the indexing chain, right-binding each
let idx_expr = parse_index_chain(input, state, idx_expr, settings.level_up())?; let idx_expr =
parse_index_chain(input, state, lib, idx_expr, settings.level_up())?;
// Indexing binds to right // Indexing binds to right
Ok(Expr::Index(Box::new((lhs, idx_expr, prev_pos)))) Ok(Expr::Index(Box::new((lhs, idx_expr, prev_pos))))
} }
@ -1228,6 +1239,7 @@ fn parse_index_chain(
fn parse_array_literal( fn parse_array_literal(
input: &mut TokenStream, input: &mut TokenStream,
state: &mut ParseState, state: &mut ParseState,
lib: &mut FunctionsLib,
settings: ParseSettings, settings: ParseSettings,
) -> Result<Expr, ParseError> { ) -> Result<Expr, ParseError> {
settings.ensure_level_within_max_limit(state.max_expr_depth)?; settings.ensure_level_within_max_limit(state.max_expr_depth)?;
@ -1249,7 +1261,7 @@ fn parse_array_literal(
break; break;
} }
_ => { _ => {
let expr = parse_expr(input, state, settings.level_up())?; let expr = parse_expr(input, state, lib, settings.level_up())?;
arr.push(expr); arr.push(expr);
} }
} }
@ -1284,6 +1296,7 @@ fn parse_array_literal(
fn parse_map_literal( fn parse_map_literal(
input: &mut TokenStream, input: &mut TokenStream,
state: &mut ParseState, state: &mut ParseState,
lib: &mut FunctionsLib,
settings: ParseSettings, settings: ParseSettings,
) -> Result<Expr, ParseError> { ) -> Result<Expr, ParseError> {
settings.ensure_level_within_max_limit(state.max_expr_depth)?; settings.ensure_level_within_max_limit(state.max_expr_depth)?;
@ -1343,7 +1356,7 @@ fn parse_map_literal(
.into_err(input.peek().unwrap().1)); .into_err(input.peek().unwrap().1));
} }
let expr = parse_expr(input, state, settings.level_up())?; let expr = parse_expr(input, state, lib, settings.level_up())?;
map.push(((Into::<ImmutableString>::into(name), pos), expr)); map.push(((Into::<ImmutableString>::into(name), pos), expr));
} }
} }
@ -1388,6 +1401,7 @@ fn parse_map_literal(
fn parse_primary( fn parse_primary(
input: &mut TokenStream, input: &mut TokenStream,
state: &mut ParseState, state: &mut ParseState,
lib: &mut FunctionsLib,
mut settings: ParseSettings, mut settings: ParseSettings,
) -> Result<Expr, ParseError> { ) -> Result<Expr, ParseError> {
let (token, token_pos) = input.peek().unwrap(); let (token, token_pos) = input.peek().unwrap();
@ -1397,7 +1411,7 @@ fn parse_primary(
let (token, _) = match token { let (token, _) = match token {
// { - block statement as expression // { - block statement as expression
Token::LeftBrace if settings.allow_stmt_expr => { Token::LeftBrace if settings.allow_stmt_expr => {
return parse_block(input, state, settings.level_up()) return parse_block(input, state, lib, settings.level_up())
.map(|block| Expr::Stmt(Box::new((block, settings.pos)))) .map(|block| Expr::Stmt(Box::new((block, settings.pos))))
} }
Token::EOF => return Err(PERR::UnexpectedEOF.into_err(settings.pos)), Token::EOF => return Err(PERR::UnexpectedEOF.into_err(settings.pos)),
@ -1414,11 +1428,11 @@ fn parse_primary(
let index = state.find_var(&s); let index = state.find_var(&s);
Expr::Variable(Box::new(((s, settings.pos), None, 0, index))) Expr::Variable(Box::new(((s, settings.pos), None, 0, index)))
} }
Token::LeftParen => parse_paren_expr(input, state, settings.level_up())?, Token::LeftParen => parse_paren_expr(input, state, lib, settings.level_up())?,
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Token::LeftBracket => parse_array_literal(input, state, settings.level_up())?, Token::LeftBracket => parse_array_literal(input, state, lib, settings.level_up())?,
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Token::MapStart => parse_map_literal(input, state, settings.level_up())?, Token::MapStart => parse_map_literal(input, state, lib, settings.level_up())?,
Token::True => Expr::True(settings.pos), Token::True => Expr::True(settings.pos),
Token::False => Expr::False(settings.pos), Token::False => Expr::False(settings.pos),
Token::LexError(err) => return Err(err.into_err(settings.pos)), Token::LexError(err) => return Err(err.into_err(settings.pos)),
@ -1445,7 +1459,7 @@ fn parse_primary(
(Expr::Variable(x), Token::LeftParen) => { (Expr::Variable(x), Token::LeftParen) => {
let ((name, pos), modules, _, _) = *x; let ((name, pos), modules, _, _) = *x;
settings.pos = pos; settings.pos = pos;
parse_call_expr(input, state, name, modules, settings.level_up())? parse_call_expr(input, state, lib, name, modules, settings.level_up())?
} }
(Expr::Property(_), _) => unreachable!(), (Expr::Property(_), _) => unreachable!(),
// module access // module access
@ -1468,7 +1482,7 @@ fn parse_primary(
// Indexing // Indexing
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
(expr, Token::LeftBracket) => { (expr, Token::LeftBracket) => {
parse_index_chain(input, state, expr, settings.level_up())? parse_index_chain(input, state, lib, expr, settings.level_up())?
} }
// Unknown postfix operator // Unknown postfix operator
(expr, token) => unreachable!( (expr, token) => unreachable!(
@ -1499,6 +1513,7 @@ fn parse_primary(
fn parse_unary( fn parse_unary(
input: &mut TokenStream, input: &mut TokenStream,
state: &mut ParseState, state: &mut ParseState,
lib: &mut FunctionsLib,
mut settings: ParseSettings, mut settings: ParseSettings,
) -> Result<Expr, ParseError> { ) -> Result<Expr, ParseError> {
let (token, token_pos) = input.peek().unwrap(); let (token, token_pos) = input.peek().unwrap();
@ -1508,14 +1523,14 @@ fn parse_unary(
match token { match token {
// If statement is allowed to act as expressions // If statement is allowed to act as expressions
Token::If if settings.allow_if_expr => Ok(Expr::Stmt(Box::new(( Token::If if settings.allow_if_expr => Ok(Expr::Stmt(Box::new((
parse_if(input, state, settings.level_up())?, parse_if(input, state, lib, settings.level_up())?,
settings.pos, settings.pos,
)))), )))),
// -expr // -expr
Token::UnaryMinus => { Token::UnaryMinus => {
let pos = eat_token(input, Token::UnaryMinus); let pos = eat_token(input, Token::UnaryMinus);
match parse_unary(input, state, settings.level_up())? { match parse_unary(input, state, lib, settings.level_up())? {
// Negative integer // Negative integer
Expr::IntegerConstant(x) => { Expr::IntegerConstant(x) => {
let (num, pos) = *x; let (num, pos) = *x;
@ -1555,13 +1570,13 @@ fn parse_unary(
// +expr // +expr
Token::UnaryPlus => { Token::UnaryPlus => {
eat_token(input, Token::UnaryPlus); eat_token(input, Token::UnaryPlus);
parse_unary(input, state, settings.level_up()) parse_unary(input, state, lib, settings.level_up())
} }
// !expr // !expr
Token::Bang => { Token::Bang => {
let pos = eat_token(input, Token::Bang); let pos = eat_token(input, Token::Bang);
let mut args = StaticVec::new(); let mut args = StaticVec::new();
let expr = parse_primary(input, state, settings.level_up())?; let expr = parse_primary(input, state, lib, settings.level_up())?;
args.push(expr); args.push(expr);
let op = "!"; let op = "!";
@ -1578,7 +1593,7 @@ fn parse_unary(
// <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
_ => parse_primary(input, state, settings.level_up()), _ => parse_primary(input, state, lib, settings.level_up()),
} }
} }
@ -1646,6 +1661,7 @@ fn make_assignment_stmt<'a>(
fn parse_op_assignment_stmt( fn parse_op_assignment_stmt(
input: &mut TokenStream, input: &mut TokenStream,
state: &mut ParseState, state: &mut ParseState,
lib: &mut FunctionsLib,
lhs: Expr, lhs: Expr,
mut settings: ParseSettings, mut settings: ParseSettings,
) -> Result<Expr, ParseError> { ) -> Result<Expr, ParseError> {
@ -1672,7 +1688,7 @@ fn parse_op_assignment_stmt(
}; };
let (_, pos) = input.next().unwrap(); let (_, pos) = input.next().unwrap();
let rhs = parse_expr(input, state, settings.level_up())?; let rhs = parse_expr(input, state, lib, settings.level_up())?;
make_assignment_stmt(op, state, lhs, rhs, pos) make_assignment_stmt(op, state, lhs, rhs, pos)
} }
@ -1884,6 +1900,7 @@ fn make_in_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result<Expr, ParseErr
fn parse_binary_op( fn parse_binary_op(
input: &mut TokenStream, input: &mut TokenStream,
state: &mut ParseState, state: &mut ParseState,
lib: &mut FunctionsLib,
parent_precedence: u8, parent_precedence: u8,
lhs: Expr, lhs: Expr,
mut settings: ParseSettings, mut settings: ParseSettings,
@ -1907,14 +1924,14 @@ fn parse_binary_op(
let (op_token, pos) = input.next().unwrap(); let (op_token, pos) = input.next().unwrap();
let rhs = parse_unary(input, state, settings)?; let rhs = parse_unary(input, state, lib, settings)?;
let next_precedence = input.peek().unwrap().0.precedence(custom); let next_precedence = input.peek().unwrap().0.precedence(custom);
// Bind to right if the next operator has higher precedence // Bind to right if the next operator has higher precedence
// If same precedence, then check if the operator binds right // If same precedence, then check if the operator binds right
let rhs = if (precedence == next_precedence && bind_right) || precedence < next_precedence { let rhs = if (precedence == next_precedence && bind_right) || precedence < next_precedence {
parse_binary_op(input, state, precedence, rhs, settings)? parse_binary_op(input, state, lib, precedence, rhs, settings)?
} else { } else {
// Otherwise bind to left (even if next operator has the same precedence) // Otherwise bind to left (even if next operator has the same precedence)
rhs rhs
@ -2001,13 +2018,14 @@ fn parse_binary_op(
fn parse_expr( fn parse_expr(
input: &mut TokenStream, input: &mut TokenStream,
state: &mut ParseState, state: &mut ParseState,
lib: &mut FunctionsLib,
mut settings: ParseSettings, mut settings: ParseSettings,
) -> Result<Expr, ParseError> { ) -> Result<Expr, ParseError> {
settings.pos = input.peek().unwrap().1; settings.pos = input.peek().unwrap().1;
settings.ensure_level_within_max_limit(state.max_expr_depth)?; settings.ensure_level_within_max_limit(state.max_expr_depth)?;
let lhs = parse_unary(input, state, settings.level_up())?; let lhs = parse_unary(input, state, lib, settings.level_up())?;
parse_binary_op(input, state, 1, lhs, settings.level_up()) parse_binary_op(input, state, lib, 1, lhs, settings.level_up())
} }
/// Make sure that the expression is not a statement expression (i.e. wrapped in `{}`). /// Make sure that the expression is not a statement expression (i.e. wrapped in `{}`).
@ -2053,6 +2071,7 @@ fn ensure_not_assignment(input: &mut TokenStream) -> Result<(), ParseError> {
fn parse_if( fn parse_if(
input: &mut TokenStream, input: &mut TokenStream,
state: &mut ParseState, state: &mut ParseState,
lib: &mut FunctionsLib,
mut settings: ParseSettings, mut settings: ParseSettings,
) -> Result<Stmt, ParseError> { ) -> Result<Stmt, ParseError> {
// if ... // if ...
@ -2061,18 +2080,18 @@ fn parse_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 = parse_expr(input, state, settings.level_up())?; let guard = parse_expr(input, state, lib, settings.level_up())?;
ensure_not_assignment(input)?; ensure_not_assignment(input)?;
let if_body = parse_block(input, state, settings.level_up())?; let if_body = parse_block(input, state, lib, settings.level_up())?;
// if guard { if_body } else ... // if guard { if_body } else ...
let else_body = if match_token(input, Token::Else).unwrap_or(false) { let else_body = if match_token(input, Token::Else).unwrap_or(false) {
Some(if let (Token::If, _) = input.peek().unwrap() { Some(if let (Token::If, _) = input.peek().unwrap() {
// if guard { if_body } else if ... // if guard { if_body } else if ...
parse_if(input, state, settings.level_up())? parse_if(input, state, lib, settings.level_up())?
} else { } else {
// if guard { if_body } else { else-body } // if guard { if_body } else { else-body }
parse_block(input, state, settings.level_up())? parse_block(input, state, lib, settings.level_up())?
}) })
} else { } else {
None None
@ -2085,6 +2104,7 @@ fn parse_if(
fn parse_while( fn parse_while(
input: &mut TokenStream, input: &mut TokenStream,
state: &mut ParseState, state: &mut ParseState,
lib: &mut FunctionsLib,
mut settings: ParseSettings, mut settings: ParseSettings,
) -> Result<Stmt, ParseError> { ) -> Result<Stmt, ParseError> {
// while ... // while ...
@ -2093,11 +2113,11 @@ fn parse_while(
// while guard { body } // while guard { body }
ensure_not_statement_expr(input, "a boolean")?; ensure_not_statement_expr(input, "a boolean")?;
let guard = parse_expr(input, state, settings.level_up())?; let guard = parse_expr(input, state, lib, settings.level_up())?;
ensure_not_assignment(input)?; ensure_not_assignment(input)?;
settings.is_breakable = true; settings.is_breakable = true;
let body = parse_block(input, state, 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))))
} }
@ -2106,6 +2126,7 @@ fn parse_while(
fn parse_loop( fn parse_loop(
input: &mut TokenStream, input: &mut TokenStream,
state: &mut ParseState, state: &mut ParseState,
lib: &mut FunctionsLib,
mut settings: ParseSettings, mut settings: ParseSettings,
) -> Result<Stmt, ParseError> { ) -> Result<Stmt, ParseError> {
// loop ... // loop ...
@ -2114,7 +2135,7 @@ fn parse_loop(
// loop { body } // loop { body }
settings.is_breakable = true; settings.is_breakable = true;
let body = parse_block(input, state, 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)))
} }
@ -2123,6 +2144,7 @@ fn parse_loop(
fn parse_for( fn parse_for(
input: &mut TokenStream, input: &mut TokenStream,
state: &mut ParseState, state: &mut ParseState,
lib: &mut FunctionsLib,
mut settings: ParseSettings, mut settings: ParseSettings,
) -> Result<Stmt, ParseError> { ) -> Result<Stmt, ParseError> {
// for ... // for ...
@ -2155,13 +2177,13 @@ fn parse_for(
// 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 = parse_expr(input, state, settings.level_up())?; let expr = parse_expr(input, state, lib, settings.level_up())?;
let prev_stack_len = state.stack.len(); let prev_stack_len = state.stack.len();
state.stack.push((name.clone(), ScopeEntryType::Normal)); state.stack.push((name.clone(), ScopeEntryType::Normal));
settings.is_breakable = true; settings.is_breakable = true;
let body = parse_block(input, state, settings.level_up())?; let body = parse_block(input, state, lib, settings.level_up())?;
state.stack.truncate(prev_stack_len); state.stack.truncate(prev_stack_len);
@ -2172,6 +2194,7 @@ fn parse_for(
fn parse_let( fn parse_let(
input: &mut TokenStream, input: &mut TokenStream,
state: &mut ParseState, state: &mut ParseState,
lib: &mut FunctionsLib,
var_type: ScopeEntryType, var_type: ScopeEntryType,
mut settings: ParseSettings, mut settings: ParseSettings,
) -> Result<Stmt, ParseError> { ) -> Result<Stmt, ParseError> {
@ -2199,7 +2222,7 @@ fn parse_let(
// let name = ... // let name = ...
if match_token(input, Token::Equals)? { if match_token(input, Token::Equals)? {
// let name = expr // let name = expr
let init_value = parse_expr(input, state, settings.level_up())?; let init_value = parse_expr(input, state, lib, settings.level_up())?;
match var_type { match var_type {
// let name = expr // let name = expr
@ -2237,6 +2260,7 @@ fn parse_let(
fn parse_import( fn parse_import(
input: &mut TokenStream, input: &mut TokenStream,
state: &mut ParseState, state: &mut ParseState,
lib: &mut FunctionsLib,
mut settings: ParseSettings, mut settings: ParseSettings,
) -> Result<Stmt, ParseError> { ) -> Result<Stmt, ParseError> {
// import ... // import ...
@ -2244,7 +2268,7 @@ fn parse_import(
settings.ensure_level_within_max_limit(state.max_expr_depth)?; settings.ensure_level_within_max_limit(state.max_expr_depth)?;
// import expr ... // import expr ...
let expr = parse_expr(input, state, settings.level_up())?; let expr = parse_expr(input, state, lib, settings.level_up())?;
// import expr as ... // import expr as ...
match input.next().unwrap() { match input.next().unwrap() {
@ -2273,6 +2297,7 @@ fn parse_import(
fn parse_export( fn parse_export(
input: &mut TokenStream, input: &mut TokenStream,
state: &mut ParseState, state: &mut ParseState,
lib: &mut FunctionsLib,
mut settings: ParseSettings, mut settings: ParseSettings,
) -> Result<Stmt, ParseError> { ) -> Result<Stmt, ParseError> {
settings.pos = eat_token(input, Token::Export); settings.pos = eat_token(input, Token::Export);
@ -2333,6 +2358,7 @@ fn parse_export(
fn parse_block( fn parse_block(
input: &mut TokenStream, input: &mut TokenStream,
state: &mut ParseState, state: &mut ParseState,
lib: &mut FunctionsLib,
mut settings: ParseSettings, mut settings: ParseSettings,
) -> Result<Stmt, ParseError> { ) -> Result<Stmt, ParseError> {
// Must start with { // Must start with {
@ -2357,7 +2383,12 @@ fn parse_block(
while !match_token(input, Token::RightBrace)? { while !match_token(input, Token::RightBrace)? {
// Parse statements inside the block // Parse statements inside the block
settings.is_global = false; settings.is_global = false;
let stmt = parse_stmt(input, state, settings.level_up())?;
let stmt = if let Some(s) = parse_stmt(input, state, lib, settings.level_up())? {
s
} else {
continue;
};
// See if it needs a terminating semicolon // See if it needs a terminating semicolon
let need_semicolon = !stmt.is_self_terminated(); let need_semicolon = !stmt.is_self_terminated();
@ -2402,13 +2433,14 @@ fn parse_block(
fn parse_expr_stmt( fn parse_expr_stmt(
input: &mut TokenStream, input: &mut TokenStream,
state: &mut ParseState, state: &mut ParseState,
lib: &mut FunctionsLib,
mut settings: ParseSettings, mut settings: ParseSettings,
) -> Result<Stmt, ParseError> { ) -> Result<Stmt, ParseError> {
settings.pos = input.peek().unwrap().1; settings.pos = input.peek().unwrap().1;
settings.ensure_level_within_max_limit(state.max_expr_depth)?; settings.ensure_level_within_max_limit(state.max_expr_depth)?;
let expr = parse_expr(input, state, settings.level_up())?; let expr = parse_expr(input, state, lib, settings.level_up())?;
let expr = parse_op_assignment_stmt(input, state, expr, settings.level_up())?; let expr = parse_op_assignment_stmt(input, state, lib, expr, settings.level_up())?;
Ok(Stmt::Expr(Box::new(expr))) Ok(Stmt::Expr(Box::new(expr)))
} }
@ -2416,12 +2448,13 @@ fn parse_expr_stmt(
fn parse_stmt( fn parse_stmt(
input: &mut TokenStream, input: &mut TokenStream,
state: &mut ParseState, state: &mut ParseState,
lib: &mut FunctionsLib,
mut settings: ParseSettings, mut settings: ParseSettings,
) -> Result<Stmt, ParseError> { ) -> Result<Option<Stmt>, ParseError> {
use ScopeEntryType::{Constant, Normal}; use ScopeEntryType::{Constant, Normal};
let (token, token_pos) = match input.peek().unwrap() { let (token, token_pos) = match input.peek().unwrap() {
(Token::EOF, pos) => return Ok(Stmt::Noop(*pos)), (Token::EOF, pos) => return Ok(Some(Stmt::Noop(*pos))),
x => x, x => x,
}; };
settings.pos = *token_pos; settings.pos = *token_pos;
@ -2429,28 +2462,71 @@ fn parse_stmt(
match token { match token {
// Semicolon - empty statement // Semicolon - empty statement
Token::SemiColon => Ok(Stmt::Noop(settings.pos)), Token::SemiColon => Ok(Some(Stmt::Noop(settings.pos))),
Token::LeftBrace => parse_block(input, state, settings.level_up()), Token::LeftBrace => parse_block(input, state, lib, settings.level_up()).map(Some),
// fn ... // fn ...
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
Token::Fn if !settings.is_global => Err(PERR::WrongFnDefinition.into_err(settings.pos)), Token::Fn if !settings.is_global => Err(PERR::WrongFnDefinition.into_err(settings.pos)),
#[cfg(not(feature = "no_function"))]
Token::Fn => unreachable!(),
Token::If => parse_if(input, state, settings.level_up()), #[cfg(not(feature = "no_function"))]
Token::While => parse_while(input, state, settings.level_up()), Token::Fn | Token::Private => {
Token::Loop => parse_loop(input, state, settings.level_up()), let access = if matches!(token, Token::Private) {
Token::For => parse_for(input, state, settings.level_up()), eat_token(input, Token::Private);
FnAccess::Private
} else {
FnAccess::Public
};
match input.next().unwrap() {
(Token::Fn, pos) => {
let mut state = ParseState::new(
state.engine,
state.max_function_expr_depth,
state.max_function_expr_depth,
);
let settings = ParseSettings {
allow_if_expr: true,
allow_stmt_expr: true,
allow_anonymous_fn: true,
is_global: false,
is_breakable: false,
level: 0,
pos: pos,
};
let func = parse_fn(input, &mut state, lib, access, settings)?;
// Qualifiers (none) + function name + number of arguments.
let hash = calc_fn_hash(empty(), &func.name, func.params.len(), empty());
lib.insert(hash, func);
Ok(None)
}
(_, pos) => Err(PERR::MissingToken(
Token::Fn.into(),
format!("following '{}'", Token::Private.syntax()),
)
.into_err(pos)),
}
}
Token::If => parse_if(input, state, lib, settings.level_up()).map(Some),
Token::While => parse_while(input, state, lib, settings.level_up()).map(Some),
Token::Loop => parse_loop(input, state, lib, settings.level_up()).map(Some),
Token::For => parse_for(input, state, lib, settings.level_up()).map(Some),
Token::Continue if settings.is_breakable => { Token::Continue if settings.is_breakable => {
let pos = eat_token(input, Token::Continue); let pos = eat_token(input, Token::Continue);
Ok(Stmt::Continue(pos)) Ok(Some(Stmt::Continue(pos)))
} }
Token::Break if settings.is_breakable => { Token::Break if settings.is_breakable => {
let pos = eat_token(input, Token::Break); let pos = eat_token(input, Token::Break);
Ok(Stmt::Break(pos)) Ok(Some(Stmt::Break(pos)))
} }
Token::Continue | Token::Break => Err(PERR::LoopBreak.into_err(settings.pos)), Token::Continue | Token::Break => Err(PERR::LoopBreak.into_err(settings.pos)),
@ -2463,38 +2539,41 @@ fn parse_stmt(
match input.peek().unwrap() { match input.peek().unwrap() {
// `return`/`throw` at <EOF> // `return`/`throw` at <EOF>
(Token::EOF, pos) => Ok(Stmt::ReturnWithVal(Box::new(((return_type, *pos), None)))), (Token::EOF, pos) => Ok(Some(Stmt::ReturnWithVal(Box::new((
(return_type, *pos),
None,
))))),
// `return;` or `throw;` // `return;` or `throw;`
(Token::SemiColon, _) => Ok(Stmt::ReturnWithVal(Box::new(( (Token::SemiColon, _) => Ok(Some(Stmt::ReturnWithVal(Box::new((
(return_type, settings.pos), (return_type, settings.pos),
None, None,
)))), ))))),
// `return` or `throw` with expression // `return` or `throw` with expression
(_, _) => { (_, _) => {
let expr = parse_expr(input, state, settings.level_up())?; let expr = parse_expr(input, state, lib, settings.level_up())?;
let pos = expr.position(); let pos = expr.position();
Ok(Stmt::ReturnWithVal(Box::new(( Ok(Some(Stmt::ReturnWithVal(Box::new((
(return_type, pos), (return_type, pos),
Some(expr), Some(expr),
)))) )))))
} }
} }
} }
Token::Let => parse_let(input, state, Normal, settings.level_up()), Token::Let => parse_let(input, state, lib, Normal, settings.level_up()).map(Some),
Token::Const => parse_let(input, state, Constant, settings.level_up()), Token::Const => parse_let(input, state, lib, Constant, settings.level_up()).map(Some),
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
Token::Import => parse_import(input, state, settings.level_up()), Token::Import => parse_import(input, state, lib, settings.level_up()).map(Some),
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
Token::Export if !settings.is_global => Err(PERR::WrongExport.into_err(settings.pos)), Token::Export if !settings.is_global => Err(PERR::WrongExport.into_err(settings.pos)),
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
Token::Export => parse_export(input, state, settings.level_up()), Token::Export => parse_export(input, state, lib, settings.level_up()).map(Some),
_ => parse_expr_stmt(input, state, settings.level_up()), _ => parse_expr_stmt(input, state, lib, settings.level_up()).map(Some),
} }
} }
@ -2503,10 +2582,10 @@ fn parse_stmt(
fn parse_fn( fn parse_fn(
input: &mut TokenStream, input: &mut TokenStream,
state: &mut ParseState, state: &mut ParseState,
lib: &mut FunctionsLib,
access: FnAccess, access: FnAccess,
mut settings: ParseSettings, mut settings: ParseSettings,
) -> Result<ScriptFnDef, ParseError> { ) -> Result<ScriptFnDef, ParseError> {
settings.pos = eat_token(input, Token::Fn);
settings.ensure_level_within_max_limit(state.max_expr_depth)?; settings.ensure_level_within_max_limit(state.max_expr_depth)?;
let name = match input.next().unwrap() { let name = match input.next().unwrap() {
@ -2575,7 +2654,7 @@ fn parse_fn(
let body = match input.peek().unwrap() { let body = match input.peek().unwrap() {
(Token::LeftBrace, _) => { (Token::LeftBrace, _) => {
settings.is_breakable = false; settings.is_breakable = false;
parse_block(input, state, settings.level_up())? parse_block(input, state, lib, settings.level_up())?
} }
(_, pos) => return Err(PERR::FnMissingBody(name).into_err(*pos)), (_, pos) => return Err(PERR::FnMissingBody(name).into_err(*pos)),
}; };
@ -2598,16 +2677,18 @@ impl Engine {
scope: &Scope, scope: &Scope,
optimization_level: OptimizationLevel, optimization_level: OptimizationLevel,
) -> Result<AST, ParseError> { ) -> Result<AST, ParseError> {
let mut state = ParseState::new(self, self.max_expr_depth); let mut functions = Default::default();
let mut state = ParseState::new(self, self.max_expr_depth, self.max_function_expr_depth);
let settings = ParseSettings { let settings = ParseSettings {
allow_if_expr: false, allow_if_expr: false,
allow_stmt_expr: false, allow_stmt_expr: false,
allow_anonymous_fn: false,
is_global: true, is_global: true,
is_breakable: false, is_breakable: false,
level: 0, level: 0,
pos: Position::none(), pos: Position::none(),
}; };
let expr = parse_expr(input, &mut state, settings)?; let expr = parse_expr(input, &mut state, &mut functions, settings)?;
match input.peek().unwrap() { match input.peek().unwrap() {
(Token::EOF, _) => (), (Token::EOF, _) => (),
@ -2632,60 +2713,26 @@ impl Engine {
&self, &self,
input: &mut TokenStream, input: &mut TokenStream,
) -> Result<(Vec<Stmt>, Vec<ScriptFnDef>), ParseError> { ) -> Result<(Vec<Stmt>, Vec<ScriptFnDef>), ParseError> {
let mut statements = Vec::<Stmt>::new(); let mut statements: Vec<Stmt> = Default::default();
let mut functions = HashMap::<u64, ScriptFnDef, _>::with_hasher(StraightHasherBuilder); let mut functions = Default::default();
let mut state = ParseState::new(self, self.max_expr_depth); let mut state = ParseState::new(self, self.max_expr_depth, self.max_function_expr_depth);
while !input.peek().unwrap().0.is_eof() { while !input.peek().unwrap().0.is_eof() {
// Collect all the function definitions
#[cfg(not(feature = "no_function"))]
{
let (access, must_be_fn) = if match_token(input, Token::Private)? {
(FnAccess::Private, true)
} else {
(FnAccess::Public, false)
};
match input.peek().unwrap() {
(Token::Fn, pos) => {
let mut state = ParseState::new(self, self.max_function_expr_depth);
let settings = ParseSettings {
allow_if_expr: true,
allow_stmt_expr: true,
is_global: false,
is_breakable: false,
level: 0,
pos: *pos,
};
let func = parse_fn(input, &mut state, access, settings)?;
// Qualifiers (none) + function name + number of arguments.
let hash = calc_fn_hash(empty(), &func.name, func.params.len(), empty());
functions.insert(hash, func);
continue;
}
(_, pos) if must_be_fn => {
return Err(PERR::MissingToken(
Token::Fn.into(),
format!("following '{}'", Token::Private.syntax()),
)
.into_err(*pos))
}
_ => (),
}
}
// Actual statement
let settings = ParseSettings { let settings = ParseSettings {
allow_if_expr: true, allow_if_expr: true,
allow_stmt_expr: true, allow_stmt_expr: true,
allow_anonymous_fn: true,
is_global: true, is_global: true,
is_breakable: false, is_breakable: false,
level: 0, level: 0,
pos: Position::none(), pos: Position::none(),
}; };
let stmt = parse_stmt(input, &mut state, settings)?;
let stmt = if let Some(s) = parse_stmt(input, &mut state, &mut functions, settings)? {
s
} else {
continue;
};
let need_semicolon = !stmt.is_self_terminated(); let need_semicolon = !stmt.is_self_terminated();