in rhai/src/parser.rs [3342:3619]
fn parse_stmt(
&self,
input: &mut TokenStream,
state: &mut ParseState,
lib: &mut FnLib,
settings: ParseSettings,
) -> ParseResult<Stmt> {
use AccessMode::{ReadOnly, ReadWrite};
let mut settings = settings;
#[cfg(not(feature = "no_function"))]
#[cfg(feature = "metadata")]
let comments = {
let mut comments = StaticVec::<Identifier>::new();
let mut comments_pos = Position::NONE;
let mut buf = Identifier::new();
// Handle doc-comments.
while let (Token::Comment(ref comment), pos) = input.peek().expect(NEVER_ENDS) {
if comments_pos.is_none() {
comments_pos = *pos;
}
if !crate::tokenizer::is_doc_comment(comment) {
unreachable!("doc-comment expected but gets {:?}", comment);
}
if !settings.has_flag(ParseSettingFlags::GLOBAL_LEVEL) {
return Err(PERR::WrongDocComment.into_err(comments_pos));
}
match input.next().expect(NEVER_ENDS) {
(Token::Comment(comment), pos) => {
if comment.contains('\n') {
// Assume block comment
if !buf.is_empty() {
comments.push(buf.clone());
buf.clear();
}
let c =
unindent_block_comment(*comment, pos.position().unwrap_or(1) - 1);
comments.push(c.into());
} else {
if !buf.is_empty() {
buf.push('\n');
}
buf.push_str(&comment);
}
match input.peek().expect(NEVER_ENDS) {
(Token::Fn | Token::Private, ..) => break,
(Token::Comment(..), ..) => (),
_ => return Err(PERR::WrongDocComment.into_err(comments_pos)),
}
}
(token, ..) => unreachable!("Token::Comment expected but gets {:?}", token),
}
}
if !buf.is_empty() {
comments.push(buf);
}
comments
};
let (token, token_pos) = match input.peek().expect(NEVER_ENDS) {
(Token::EOF, pos) => return Ok(Stmt::Noop(*pos)),
(x, pos) => (x, *pos),
};
settings.pos = token_pos;
match token {
// ; - empty statement
Token::SemiColon => {
eat_token(input, Token::SemiColon);
Ok(Stmt::Noop(token_pos))
}
// { - statements block
Token::LeftBrace => Ok(self.parse_block(input, state, lib, settings.level_up()?)?),
// fn ...
#[cfg(not(feature = "no_function"))]
Token::Fn if !settings.has_flag(ParseSettingFlags::GLOBAL_LEVEL) => {
Err(PERR::WrongFnDefinition.into_err(token_pos))
}
#[cfg(not(feature = "no_function"))]
Token::Fn | Token::Private => {
let access = if matches!(token, Token::Private) {
eat_token(input, Token::Private);
crate::FnAccess::Private
} else {
crate::FnAccess::Public
};
match input.next().expect(NEVER_ENDS) {
(Token::Fn, pos) => {
// Build new parse state
let new_state = &mut ParseState::new(
state.external_constants,
state.interned_strings,
state.tokenizer_control.clone(),
);
#[cfg(not(feature = "no_module"))]
{
// Do not allow storing an index to a globally-imported module
// just in case the function is separated from this `AST`.
//
// Keep them in `global_imports` instead so that strict variables
// mode will not complain.
new_state.global_imports.clone_from(&state.global_imports);
new_state
.global_imports
.get_or_insert_with(Default::default)
.extend(state.imports.as_deref().into_iter().flatten().cloned());
}
// Brand new options
let options = self.options | (settings.options & LangOptions::STRICT_VAR);
// Brand new flags, turn on function scope
let flags = ParseSettingFlags::FN_SCOPE
| (settings.flags
& ParseSettingFlags::DISALLOW_UNQUOTED_MAP_PROPERTIES);
let new_settings = ParseSettings {
flags,
level: 0,
options,
pos,
#[cfg(not(feature = "unchecked"))]
max_expr_depth: self.max_function_expr_depth(),
};
let f = self.parse_fn(
input,
new_state,
lib,
new_settings,
access,
#[cfg(feature = "metadata")]
comments,
)?;
let hash = calc_fn_hash(None, &f.name, f.params.len());
#[cfg(not(feature = "no_object"))]
let hash = if let Some(ref this_type) = f.this_type {
crate::calc_typed_method_hash(hash, this_type)
} else {
hash
};
if !lib.is_empty() && lib.contains_key(&hash) {
return Err(PERR::FnDuplicatedDefinition(
f.name.to_string(),
f.params.len(),
)
.into_err(pos));
}
lib.insert(hash, f.into());
Ok(Stmt::Noop(pos))
}
(.., pos) => Err(PERR::MissingToken(
Token::Fn.into(),
format!("following '{}'", Token::Private),
)
.into_err(pos)),
}
}
Token::If => self.parse_if(input, state, lib, settings.level_up()?),
Token::Switch => self.parse_switch(input, state, lib, settings.level_up()?),
Token::While | Token::Loop if self.allow_looping() => {
self.parse_while_loop(input, state, lib, settings.level_up()?)
}
Token::Do if self.allow_looping() => {
self.parse_do(input, state, lib, settings.level_up()?)
}
Token::For if self.allow_looping() => {
self.parse_for(input, state, lib, settings.level_up()?)
}
Token::Continue
if self.allow_looping() && settings.has_flag(ParseSettingFlags::BREAKABLE) =>
{
let pos = eat_token(input, Token::Continue);
Ok(Stmt::BreakLoop(None, ASTFlags::empty(), pos))
}
Token::Break
if self.allow_looping() && settings.has_flag(ParseSettingFlags::BREAKABLE) =>
{
let pos = eat_token(input, Token::Break);
let expr = match input.peek().expect(NEVER_ENDS) {
// `break` at <EOF>
(Token::EOF, ..) => None,
// `break` at end of block
(Token::RightBrace, ..) => None,
// `break;`
(Token::SemiColon, ..) => None,
// `break` with expression
_ => Some(
self.parse_expr(input, state, lib, settings.level_up()?)?
.into(),
),
};
Ok(Stmt::BreakLoop(expr, ASTFlags::BREAK, pos))
}
Token::Continue | Token::Break if self.allow_looping() => {
Err(PERR::LoopBreak.into_err(token_pos))
}
Token::Return | Token::Throw => {
let (return_type, token_pos) = input
.next()
.map(|(token, pos)| {
let flags = match token {
Token::Return => ASTFlags::empty(),
Token::Throw => ASTFlags::BREAK,
token => unreachable!(
"Token::Return or Token::Throw expected but gets {:?}",
token
),
};
(flags, pos)
})
.expect(NEVER_ENDS);
match input.peek().expect(NEVER_ENDS) {
// `return`/`throw` at <EOF>
(Token::EOF, ..) => Ok(Stmt::Return(None, return_type, token_pos)),
// `return`/`throw` at end of block
(Token::RightBrace, ..)
if !settings.has_flag(ParseSettingFlags::GLOBAL_LEVEL) =>
{
Ok(Stmt::Return(None, return_type, token_pos))
}
// `return;` or `throw;`
(Token::SemiColon, ..) => Ok(Stmt::Return(None, return_type, token_pos)),
// `return` or `throw` with expression
_ => {
let expr = self.parse_expr(input, state, lib, settings.level_up()?)?;
Ok(Stmt::Return(Some(expr.into()), return_type, token_pos))
}
}
}
Token::Try => self.parse_try_catch(input, state, lib, settings.level_up()?),
Token::Let => self.parse_let(input, state, lib, settings.level_up()?, ReadWrite, false),
Token::Const => {
self.parse_let(input, state, lib, settings.level_up()?, ReadOnly, false)
}
#[cfg(not(feature = "no_module"))]
Token::Import => self.parse_import(input, state, lib, settings.level_up()?),
#[cfg(not(feature = "no_module"))]
Token::Export if !settings.has_flag(ParseSettingFlags::GLOBAL_LEVEL) => {
Err(PERR::WrongExport.into_err(token_pos))
}
#[cfg(not(feature = "no_module"))]
Token::Export => self.parse_export(input, state, lib, settings.level_up()?),
_ => self.parse_expr_stmt(input, state, lib, settings.level_up()?),
}
}