fn parse_fn_call()

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))
                }
            }
        }
    }