luau-syntax 0.732.0

Luau lexer, parser, AST, CST, and source utilities
Documentation
use super::*;

impl<'source, 'ast, 'name, 'names> Parser<'source, 'ast, 'name, 'names>
where
    'name: 'ast,
{
    pub(in crate::parser) fn parse_function_literal(&mut self) -> Result<Expression<'ast>> {
        let location = self.current_location();
        self.parse_function_literal_with_debug_name(ParsedAttributes::default(), location, None)
    }

    pub(in crate::parser) fn parse_function_literal_with_attributes(
        &mut self,
        attributes: ParsedAttributes<'ast>,
        location: Location,
    ) -> Result<Expression<'ast>> {
        self.parse_function_literal_with_debug_name(attributes, location, None)
    }

    pub(in crate::parser) fn parse_function_literal_with_debug_name(
        &mut self,
        attributes: ParsedAttributes<'ast>,
        location: Location,
        debug_name: Option<AstName<'ast>>,
    ) -> Result<Expression<'ast>> {
        let function_keyword = self.current_position();
        self.advance();
        let parsed = self.parse_function_after_name(FunctionParseContext {
            attributes,
            location,
            match_location: location,
            function_keyword,
            self_parameter: None,
            debug_name,
        })?;
        Ok(self
            .arena
            .alloc_expression_function_literal_direct(parsed.function.location, parsed.function))
    }

    pub(in crate::parser) fn parse_function_after_name(
        &mut self,
        context: FunctionParseContext<'ast>,
    ) -> Result<ParsedFunction<'ast>> {
        self.parse_function_after_name_inner(context, |_| ())
            .map(|parsed| ParsedFunction {
                function: parsed.function,
            })
    }

    pub(in crate::parser) fn parse_local_function_after_name(
        &mut self,
        context: FunctionParseContext<'ast>,
        local: FunctionLocalBinding<'ast>,
    ) -> Result<ParsedLocalFunction<'ast>> {
        self.parse_function_after_name_inner(context, |parser| {
            parser.declare_local(
                local.name,
                local.location,
                None,
                local.is_const,
                local.is_exported,
            )
        })
        .map(|parsed| ParsedLocalFunction {
            function: parsed.function,
            local: parsed.local,
        })
    }

    fn parse_function_after_name_inner<L>(
        &mut self,
        context: FunctionParseContext<'ast>,
        declare_local: impl FnOnce(&mut Self) -> L,
    ) -> Result<ParsedFunctionWithLocal<'ast, L>> {
        let FunctionParseContext {
            attributes,
            location,
            match_location,
            function_keyword,
            self_parameter,
            debug_name,
        } = context;
        self.with_match_recovery_stop(MatchRecoveryStop::ReservedEnd, |parser| {
            let generics = parser.parse_optional_generic_parameters(false)?;
            let paren_location = parser.current_location();
            let paren = parser.current;
            parser.expect_function_left_paren()?;
            let mut bindings = parser.temp_bindings();
            let mut argument_commas = parser.temp_positions();
            let parameters =
                parser.with_match_recovery_stop(MatchRecoveryStop::RightParen, |parser| {
                    let parameters = if parser.current.kind() != TokenKind::RightParen {
                        parser.parse_binding_list_into(
                            &mut bindings,
                            true,
                            &mut argument_commas,
                            false,
                        )?
                    } else {
                        ParsedBindingList {
                            variadic: false,
                            vararg_location: Location::zero(),
                            vararg_annotation: None,
                            vararg_annotation_colon: Position::missing(),
                        }
                    };
                    let arg_location = (paren.kind() == TokenKind::LeftParen
                        && parser.current.kind() == TokenKind::RightParen)
                        .then(|| {
                            Location::new(paren_location.begin, parser.current_token_location().end)
                        });
                    if parser.current.kind() != TokenKind::RightParen {
                        let error = parser.matched_token_error(")", "(", paren_location);
                        parser.report_parse_error(error);
                        parser.recover_match_on_line(Token::RightParen);
                    } else {
                        parser.advance();
                    }
                    Ok((parameters, arg_location))
                })?;
            let (parameters, arg_location) = parameters;
            let (return_annotation, return_specifier) =
                parser.parse_optional_return_annotation_with_colon()?;
            let body_begin = parser.previous_token_end_position();
            let local = declare_local(parser);

            let locals_begin = parser.locals.stack.len();
            parser.contexts.functions.push(FunctionContext {
                loop_depth: 0,
                vararg: parameters.variadic,
            });
            let self_parameter = if let Some(location) = self_parameter {
                let name = parser.name_self;
                Some(parser.declare_local(name, location, None, false, false))
            } else {
                None
            };
            let mut args = parser.temp_locals();
            for binding in bindings.iter() {
                args.push_back(parser.declare_local(
                    binding.name,
                    binding.name_location,
                    binding.annotation,
                    binding.is_const,
                    false,
                ));
            }

            let body =
                parser.parse_block_until_from(&["end"], "function", match_location, body_begin);
            parser.contexts.functions.pop();
            parser.restore_locals(locals_begin);
            let function_depth = parser.contexts.functions.len() + 1;
            let end_location = parser.current_token_location();
            let mut body = body?;
            body.has_end = parser.expect_match_end_with_lookahead("function", match_location);
            let body = parser.arena.alloc_block(body);
            let attr_lists = attributes.attr_lists;
            let attribute_values = attributes.attributes;
            let cst = parser.cst.enabled().then(|| CstExprFunction {
                attr_lists,
                function_keyword,
                open_generics: generics.open,
                generics_commas: parser.arena.alloc_slice_copy(&generics.commas),
                close_generics: generics.close,
                argument_annotation_colons: {
                    let mut argument_annotation_colons = argument_commas.nested();
                    for binding in bindings.iter() {
                        argument_annotation_colons.push_back(binding.colon_position);
                    }
                    parser
                        .arena
                        .alloc_slice_copy(argument_annotation_colons.as_slice())
                },
                argument_commas: parser.arena.alloc_slice_copy(argument_commas.as_slice()),
                vararg_annotation_colon: parameters.vararg_annotation_colon,
                return_specifier: return_specifier.unwrap_or(Position::missing()),
            });
            let vararg_annotation = parameters.vararg_annotation;
            let attributes = parser.arena.alloc_slice_copy(&attribute_values);
            let function = parser.alloc_function_with_cst(
                Function {
                    location: Location::new(location.begin, end_location.end),
                    attributes,
                    generics: parser.arena.alloc_slice_copy(&generics.types),
                    generic_packs: parser.arena.alloc_slice_copy(&generics.type_packs),
                    self_parameter,
                    args: parser.arena.alloc_slice_copy(args.as_slice()),
                    vararg: parameters.variadic,
                    vararg_location: parameters.vararg_location,
                    body,
                    function_depth,
                    debug_name,
                    return_annotation,
                    vararg_annotation,
                    arg_location,
                },
                cst.map(CstNode::ExprFunction),
            );
            Ok(ParsedFunctionWithLocal { function, local })
        })
    }
}

pub(super) struct ParsedFunction<'ast> {
    pub(super) function: &'ast mut Function<'ast>,
}

pub(super) struct ParsedLocalFunction<'ast> {
    pub(super) function: &'ast mut Function<'ast>,
    pub(super) local: &'ast Local<'ast>,
}

struct ParsedFunctionWithLocal<'ast, L> {
    function: &'ast mut Function<'ast>,
    local: L,
}

pub(in crate::parser) struct FunctionParseContext<'ast> {
    pub(super) attributes: ParsedAttributes<'ast>,
    pub(super) location: Location,
    pub(super) match_location: Location,
    pub(super) function_keyword: Position,
    pub(super) self_parameter: Option<Location>,
    pub(super) debug_name: Option<AstName<'ast>>,
}

pub(in crate::parser) struct FunctionLocalBinding<'ast> {
    pub(super) name: AstName<'ast>,
    pub(super) location: Location,
    pub(super) is_const: bool,
    pub(super) is_exported: bool,
}