in rhai/src/parser.rs [620:812]
fn parse_fn_call(
&self,
input: &mut TokenStream,
state: &mut ParseState,
lib: &mut FnLib,
settings: ParseSettings,
id: ImmutableString,
no_args: bool,
capture_parent_scope: bool,
namespace: Namespace,
) -> ParseResult<Expr> {
let (token, token_pos) = if no_args {
&(Token::RightParen, Position::NONE)
} else {
input.peek().expect(NEVER_ENDS)
};
let mut _namespace = namespace;
let mut args = FnArgsVec::new_const();
match token {
// id( <EOF>
Token::EOF => {
return Err(PERR::MissingToken(
Token::RightParen.into(),
format!("to close the arguments list of this function call '{id}'"),
)
.into_err(*token_pos))
}
// id( <error>
Token::LexError(err) => return Err(err.clone().into_err(*token_pos)),
// id()
Token::RightParen => {
if !no_args {
eat_token(input, Token::RightParen);
}
#[cfg(not(feature = "no_module"))]
let hash = if _namespace.is_empty() {
calc_fn_hash(None, &id, 0)
} else {
let root = _namespace.root();
let index = state.find_module(root);
let is_global = false;
#[cfg(not(feature = "no_function"))]
#[cfg(not(feature = "no_module"))]
let is_global = is_global || root == crate::engine::KEYWORD_GLOBAL;
if settings.has_option(LangOptions::STRICT_VAR)
&& index.is_none()
&& !is_global
&& !state
.global_imports
.as_deref()
.into_iter()
.flatten()
.any(|m| m.as_str() == root)
&& !self
.global_sub_modules
.as_ref()
.map_or(false, |m| m.contains_key(root))
{
return Err(
PERR::ModuleUndefined(root.into()).into_err(_namespace.position())
);
}
_namespace.set_index(index);
calc_fn_hash(_namespace.iter().map(Ident::as_str), &id, 0)
};
#[cfg(feature = "no_module")]
let hash = calc_fn_hash(None, &id, 0);
let hashes = if is_valid_function_name(&id) {
FnCallHashes::from_hash(hash)
} else {
FnCallHashes::from_native_only(hash)
};
args.shrink_to_fit();
return Ok(FnCallExpr {
name: state.get_interned_string(id),
capture_parent_scope,
op_token: None,
namespace: _namespace,
hashes,
args,
}
.into_fn_call_expr(settings.pos));
}
// id...
_ => (),
}
let settings = settings.level_up()?;
loop {
match input.peek().expect(NEVER_ENDS) {
// id(...args, ) - handle trailing comma
(Token::RightParen, ..) => (),
_ => args.push(self.parse_expr(input, state, lib, settings)?),
}
match input.peek().expect(NEVER_ENDS) {
// id(...args)
(Token::RightParen, ..) => {
eat_token(input, Token::RightParen);
#[cfg(not(feature = "no_module"))]
let hash = if _namespace.is_empty() {
calc_fn_hash(None, &id, args.len())
} else {
let root = _namespace.root();
let index = state.find_module(root);
#[cfg(not(feature = "no_function"))]
#[cfg(not(feature = "no_module"))]
let is_global = root == crate::engine::KEYWORD_GLOBAL;
#[cfg(any(feature = "no_function", feature = "no_module"))]
let is_global = false;
if settings.has_option(LangOptions::STRICT_VAR)
&& index.is_none()
&& !is_global
&& !state
.global_imports
.as_deref()
.into_iter()
.flatten()
.any(|m| m.as_str() == root)
&& !self
.global_sub_modules
.as_ref()
.map_or(false, |m| m.contains_key(root))
{
return Err(
PERR::ModuleUndefined(root.into()).into_err(_namespace.position())
);
}
_namespace.set_index(index);
calc_fn_hash(_namespace.iter().map(Ident::as_str), &id, args.len())
};
#[cfg(feature = "no_module")]
let hash = calc_fn_hash(None, &id, args.len());
let hashes = if is_valid_function_name(&id) {
FnCallHashes::from_hash(hash)
} else {
FnCallHashes::from_native_only(hash)
};
args.shrink_to_fit();
return Ok(FnCallExpr {
name: state.get_interned_string(id),
capture_parent_scope,
op_token: None,
namespace: _namespace,
hashes,
args,
}
.into_fn_call_expr(settings.pos));
}
// id(...args,
(Token::Comma, ..) => {
eat_token(input, Token::Comma);
}
// id(...args <EOF>
(Token::EOF, pos) => {
return Err(PERR::MissingToken(
Token::RightParen.into(),
format!("to close the arguments list of this function call '{id}'"),
)
.into_err(*pos))
}
// id(...args <error>
(Token::LexError(err), pos) => return Err(err.clone().into_err(*pos)),
// id(...args ???
(.., pos) => {
return Err(PERR::MissingToken(
Token::Comma.into(),
format!("to separate the arguments to function call '{id}'"),
)
.into_err(*pos))
}
}
}
}