Skip to main content

luaur_ast/methods/
parser_parse_if.rs

1//! Node: `cxx:Method:Luau.Ast:Ast/src/Parser.cpp:559:parseIf`
2//!
3//! Faithful port of `Parser::parseIf` — `if exp then block {elseif/else} end`.
4//! Chained `elseif` recurses through `parse_if` (guarded by the recursion
5//! counter); a trailing `else` is parsed as a block whose begin is snapped to
6//! the `else` keyword. `hasEnd` is propagated onto whichever block terminates
7//! the chain so the pretty-printer / CST consumers see the real end span.
8
9use crate::records::allocator::Allocator;
10use crate::records::ast_node::AstNode;
11use crate::records::ast_stat::AstStat;
12use crate::records::ast_stat_block::AstStatBlock;
13use crate::records::ast_stat_if::AstStatIf;
14use crate::records::lexeme::Type;
15use crate::records::location::Location;
16use crate::records::match_lexeme::MatchLexeme;
17use crate::records::parser::Parser;
18use crate::rtti::ast_node_as;
19
20impl Parser {
21    pub fn parse_if(&mut self) -> *mut AstStat {
22        let start = self.lexer.current().location;
23
24        self.next_lexeme(); // if / elseif
25
26        let cond = self.parse_expr_i32(0);
27
28        let match_then = *self.lexer.current();
29        let mut then_location: Option<Location> = None;
30        if self.expect_and_consume_type(Type::ReservedThen, "if statement") {
31            then_location = Some(match_then.location);
32        }
33
34        let thenbody = self.parse_block();
35
36        let mut elsebody: *mut AstStat = core::ptr::null_mut();
37        let mut end = start;
38        let mut else_location: Option<Location> = None;
39
40        if self.lexer.current().r#type == Type::ReservedElseif {
41            unsafe {
42                (*thenbody).has_end = true;
43            }
44            let old_recursion_count = self.recursion_counter;
45            self.increment_recursion_counter("elseif");
46            else_location = Some(self.lexer.current().location);
47            elsebody = self.parse_if();
48            end = unsafe { (*elsebody).base.location };
49            self.recursion_counter = old_recursion_count;
50        } else {
51            let mut match_then_else = match_then;
52
53            if self.lexer.current().r#type == Type::ReservedElse {
54                unsafe {
55                    (*thenbody).has_end = true;
56                }
57                else_location = Some(self.lexer.current().location);
58                match_then_else = *self.lexer.current();
59                self.next_lexeme();
60
61                let else_block = self.parse_block();
62                unsafe {
63                    (*else_block).base.base.location.begin = match_then_else.location.end;
64                }
65                elsebody = else_block as *mut AstStat;
66            }
67
68            end = self.lexer.current().location;
69
70            let has_end = self.expect_match_end_and_consume(
71                Type::ReservedEnd,
72                &MatchLexeme::new(&match_then_else),
73            );
74
75            if !elsebody.is_null() {
76                let else_block = unsafe { ast_node_as::<AstStatBlock>(elsebody as *mut AstNode) };
77                if !else_block.is_null() {
78                    unsafe {
79                        (*else_block).has_end = has_end;
80                    }
81                }
82            } else {
83                unsafe {
84                    (*thenbody).has_end = has_end;
85                }
86            }
87        }
88
89        unsafe {
90            (*self.allocator).alloc(AstStatIf::new(
91                Location::new(start.begin, end.end),
92                cond,
93                thenbody,
94                elsebody,
95                then_location,
96                else_location,
97            )) as *mut AstStat
98        }
99    }
100}