Skip to main content

luaur_ast/methods/
parser_run_parse.rs

1use crate::records::allocator::Allocator;
2use crate::records::ast_name_table::AstNameTable;
3use crate::records::lexeme::Type;
4use crate::records::parse_node_result::ParseNodeResult;
5use crate::records::parse_options::ParseOptions;
6use crate::records::parser::Parser;
7use luaur_common::macros::luau_timetrace_scope::LUAU_TIMETRACE_SCOPE;
8
9impl Parser {
10    pub fn run_parse<Node, F>(
11        buffer: &str,
12        buffer_size: usize,
13        names: &mut AstNameTable,
14        allocator: &mut Allocator,
15        options: ParseOptions,
16        f: F,
17    ) -> ParseNodeResult<Node>
18    where
19        F: FnOnce(&mut Parser) -> *mut Node,
20    {
21        LUAU_TIMETRACE_SCOPE!("Parser::parse", "Parser");
22
23        // Silence the default panic-hook noise for the parser's exception-
24        // emulation unwinds (a caught `ParseError` is a normal syntax/limit
25        // error, not a crash).
26        crate::functions::install_parse_error_panic_hook::install_parse_error_panic_hook();
27
28        let mut p = Parser::new(buffer, names, allocator as *mut Allocator, options);
29
30        // C++ try-catch is mapped to a result-like handling of ParseError panics if they occur,
31        // but the source uses a catch-block for fatal errors. In Luau's Parser, ParseError
32        // is often thrown via a panic-like mechanism in Rust or handled via explicit checks.
33        // Following the C++ logic:
34        let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
35            let expr = f(&mut p);
36            let current_lexeme = p.lexer.current();
37
38            let mut lines = current_lexeme.location.end.line;
39            if buffer_size > 0 && buffer.as_bytes()[buffer_size - 1] != b'\n' {
40                lines += 1;
41            }
42
43            let eof = p.lexer.next();
44            let mut root = expr;
45
46            if eof.r#type != Type::Eof {
47                root = core::ptr::null_mut();
48                p.parse_errors
49                    .push(crate::records::parse_error::ParseError::new(
50                        eof.location,
51                        "Expected end of file".to_string(),
52                    ));
53            }
54
55            ParseNodeResult {
56                root,
57                lines: lines as usize,
58                hotcomments: std::mem::take(&mut p.hotcomments),
59                errors: std::mem::take(&mut p.parse_errors),
60                comment_locations: std::mem::take(&mut p.comment_locations),
61                cst_node_map: core::mem::replace(
62                    &mut p.cst_node_map,
63                    luaur_common::records::dense_hash_map::DenseHashMap::new(core::ptr::null_mut()),
64                ),
65            }
66        }));
67
68        match result {
69            Ok(res) => res,
70            Err(payload) => {
71                // If it's a ParseError (the C++ catch (ParseError& err) case)
72                if let Some(err) = payload.downcast_ref::<crate::records::parse_error::ParseError>()
73                {
74                    p.parse_errors.push(err.clone());
75
76                    ParseNodeResult {
77                        root: core::ptr::null_mut(),
78                        lines: 0,
79                        hotcomments: Vec::new(),
80                        errors: std::mem::take(&mut p.parse_errors),
81                        comment_locations: Vec::new(),
82                        cst_node_map: core::mem::replace(
83                            &mut p.cst_node_map,
84                            luaur_common::records::dense_hash_map::DenseHashMap::new(
85                                core::ptr::null_mut(),
86                            ),
87                        ),
88                    }
89                } else {
90                    // Re-panic if it's not a ParseError
91                    std::panic::resume_unwind(payload);
92                }
93            }
94        }
95    }
96}