fn parse_custom_syntax()

in rhai/src/parser.rs [2542:2741]


    fn parse_custom_syntax(
        &self,
        input: &mut TokenStream,
        state: &mut ParseState,
        lib: &mut FnLib,
        settings: ParseSettings,
        key: impl Into<ImmutableString>,
        syntax: &crate::api::custom_syntax::CustomSyntax,
        pos: Position,
    ) -> ParseResult<Expr> {
        #[allow(clippy::wildcard_imports)]
        use crate::api::custom_syntax::markers::*;

        const KEYWORD_SEMICOLON: &str = Token::SemiColon.literal_syntax();
        const KEYWORD_CLOSE_BRACE: &str = Token::RightBrace.literal_syntax();

        let mut settings = settings;
        let mut inputs = StaticVec::new_const();
        let mut segments = StaticVec::new_const();
        let mut tokens = StaticVec::new_const();

        // Adjust the variables stack
        if syntax.scope_may_be_changed {
            // Add a barrier variable to the stack so earlier variables will not be matched.
            // Variable searches stop at the first barrier.
            let marker = state.get_interned_string(SCOPE_SEARCH_BARRIER_MARKER);
            state
                .stack
                .get_or_insert_with(Default::default)
                .push(marker, ());
        }

        let mut user_state = Dynamic::UNIT;
        let parse_func = &*syntax.parse;
        let mut required_token: ImmutableString = key.into();

        tokens.push(required_token.clone());
        segments.push(required_token.clone());

        loop {
            let (fwd_token, fwd_pos) = input.peek().expect(NEVER_ENDS);
            settings.pos = *fwd_pos;
            let settings = settings.level_up()?;

            required_token = match parse_func(&segments, &fwd_token.to_string(), &mut user_state) {
                Ok(Some(seg))
                    if seg.starts_with(CUSTOM_SYNTAX_MARKER_SYNTAX_VARIANT)
                        && seg.len() > CUSTOM_SYNTAX_MARKER_SYNTAX_VARIANT.len() =>
                {
                    inputs.push(Expr::StringConstant(state.get_interned_string(seg), pos));
                    break;
                }
                Ok(Some(seg)) => seg,
                Ok(None) => break,
                Err(err) => return Err(err.0.into_err(settings.pos)),
            };

            match required_token.as_str() {
                CUSTOM_SYNTAX_MARKER_IDENT => {
                    let (name, pos) = parse_var_name(input)?;
                    let name = state.get_interned_string(name);

                    let ns = Namespace::NONE;

                    segments.push(name.clone());
                    tokens.push(state.get_interned_string(CUSTOM_SYNTAX_MARKER_IDENT));
                    inputs.push(Expr::Variable((None, ns, 0, name).into(), None, pos));
                }
                CUSTOM_SYNTAX_MARKER_SYMBOL => {
                    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);
                    segments.push(symbol.clone());
                    tokens.push(state.get_interned_string(CUSTOM_SYNTAX_MARKER_SYMBOL));
                    inputs.push(Expr::StringConstant(symbol, pos));
                }
                CUSTOM_SYNTAX_MARKER_EXPR => {
                    inputs.push(self.parse_expr(input, state, lib, settings)?);
                    let keyword = state.get_interned_string(CUSTOM_SYNTAX_MARKER_EXPR);
                    segments.push(keyword.clone());
                    tokens.push(keyword);
                }
                CUSTOM_SYNTAX_MARKER_BLOCK => {
                    match self.parse_block(input, state, lib, settings)? {
                        block @ Stmt::Block(..) => {
                            inputs.push(Expr::Stmt(Box::new(block.into())));
                            let keyword = state.get_interned_string(CUSTOM_SYNTAX_MARKER_BLOCK);
                            segments.push(keyword.clone());
                            tokens.push(keyword);
                        }
                        stmt => unreachable!("Stmt::Block expected but gets {:?}", stmt),
                    }
                }
                CUSTOM_SYNTAX_MARKER_BOOL => match input.next().expect(NEVER_ENDS) {
                    (b @ (Token::True | Token::False), pos) => {
                        inputs.push(Expr::BoolConstant(b == Token::True, pos));
                        segments.push(state.get_interned_string(b.literal_syntax()));
                        tokens.push(state.get_interned_string(CUSTOM_SYNTAX_MARKER_BOOL));
                    }
                    (.., pos) => {
                        return Err(
                            PERR::MissingSymbol("Expecting 'true' or 'false'".into()).into_err(pos)
                        )
                    }
                },
                CUSTOM_SYNTAX_MARKER_INT => match input.next().expect(NEVER_ENDS) {
                    (Token::IntegerConstant(i), pos) => {
                        inputs.push(Expr::IntegerConstant(i, pos));
                        segments.push(i.to_string().into());
                        tokens.push(state.get_interned_string(CUSTOM_SYNTAX_MARKER_INT));
                    }
                    (.., pos) => {
                        return Err(
                            PERR::MissingSymbol("Expecting an integer number".into()).into_err(pos)
                        )
                    }
                },
                #[cfg(not(feature = "no_float"))]
                CUSTOM_SYNTAX_MARKER_FLOAT => match input.next().expect(NEVER_ENDS) {
                    (Token::FloatConstant(f), pos) => {
                        inputs.push(Expr::FloatConstant(f, pos));
                        segments.push(f.to_string().into());
                        tokens.push(state.get_interned_string(CUSTOM_SYNTAX_MARKER_FLOAT));
                    }
                    (.., pos) => {
                        return Err(
                            PERR::MissingSymbol("Expecting a floating-point number".into())
                                .into_err(pos),
                        )
                    }
                },
                CUSTOM_SYNTAX_MARKER_STRING => match input.next().expect(NEVER_ENDS) {
                    (Token::StringConstant(s), pos) => {
                        let s = state.get_interned_string(*s);
                        inputs.push(Expr::StringConstant(s.clone(), pos));
                        segments.push(s);
                        tokens.push(state.get_interned_string(CUSTOM_SYNTAX_MARKER_STRING));
                    }
                    (.., pos) => {
                        return Err(PERR::MissingSymbol("Expecting a string".into()).into_err(pos))
                    }
                },
                s => match input.next().expect(NEVER_ENDS) {
                    (Token::LexError(err), pos) => return Err(err.into_err(pos)),
                    (Token::Identifier(t) | Token::Reserved(t) | Token::Custom(t), ..)
                        if *t == s =>
                    {
                        segments.push(required_token.clone());
                        tokens.push(required_token.clone());
                    }
                    (t, ..) if t.is_literal() && t.literal_syntax() == s => {
                        segments.push(required_token.clone());
                        tokens.push(required_token.clone());
                    }
                    (.., pos) => {
                        return Err(PERR::MissingToken(
                            s.into(),
                            format!("for '{}' expression", segments[0]),
                        )
                        .into_err(pos))
                    }
                },
            }
        }

        inputs.shrink_to_fit();
        tokens.shrink_to_fit();

        let self_terminated = matches!(
            required_token.as_str(),
            // It is self-terminating if the last symbol is a block
            CUSTOM_SYNTAX_MARKER_BLOCK |
            // If the last symbol is `;` or `}`, it is self-terminating
            KEYWORD_SEMICOLON | KEYWORD_CLOSE_BRACE
        );

        Ok(Expr::Custom(
            crate::ast::CustomExpr {
                inputs,
                tokens,
                state: user_state,
                scope_may_be_changed: syntax.scope_may_be_changed,
                self_terminated,
            }
            .into(),
            pos,
        ))
    }