Skip to main content

luaur_ast/methods/
parser_parse_for.rs

1//! Node: `cxx:Method:Luau.Ast:Ast/src/Parser.cpp:726:parseFor`
2//!
3//! Faithful port of `Parser::parseFor` — both numeric `for i = a, b [, c] do`
4//! and generic `for a, b in exprs do` loops. The first binding is parsed before
5//! the `=`/`in` is known; numeric loops push the single control variable, while
6//! generic loops parse a binding list and an expression list, copying both into
7//! arena `AstArray`s for `AstStatForIn`. CST positions (commas / `=` / colon
8//! annotations) are recorded only under `store_cst_data`.
9
10use crate::records::allocator::Allocator;
11use crate::records::ast_array::AstArray;
12use crate::records::ast_expr::AstExpr;
13use crate::records::ast_node::AstNode;
14use crate::records::ast_stat::AstStat;
15use crate::records::ast_stat_for::AstStatFor;
16use crate::records::ast_stat_for_in::AstStatForIn;
17use crate::records::cst_node::CstNode;
18use crate::records::cst_stat_for::CstStatFor;
19use crate::records::cst_stat_for_in::CstStatForIn;
20use crate::records::lexeme::Type;
21use crate::records::location::Location;
22use crate::records::match_lexeme::MatchLexeme;
23use crate::records::parser::Parser;
24use crate::records::position::Position;
25use crate::records::temp_vector::TempVector;
26
27impl Parser {
28    pub fn parse_for(&mut self) -> *mut AstStat {
29        let start = self.lexer.current().location;
30
31        self.next_lexeme(); // for
32
33        let varname = self.parse_binding(false);
34
35        if self.lexer.current().r#type == Type(b'=' as i32) {
36            let equals_position = self.lexer.current().location.begin;
37            self.next_lexeme();
38
39            let from = self.parse_expr_i32(0);
40
41            let has_end_comma = self.expect_and_consume_char(',', "index range");
42            let end_comma_position = if has_end_comma {
43                self.lexer.previous_location().begin
44            } else {
45                Position::missing()
46            };
47
48            let to = self.parse_expr_i32(0);
49
50            let mut step_comma_position = Position::missing();
51            let mut step: *mut AstExpr = core::ptr::null_mut();
52
53            if self.lexer.current().r#type == Type(b',' as i32) {
54                step_comma_position = self.lexer.current().location.begin;
55                self.next_lexeme();
56
57                step = self.parse_expr_i32(0);
58            }
59
60            let match_do = *self.lexer.current();
61            let has_do = self.expect_and_consume_type(Type::ReservedDo, "for loop");
62
63            let locals_begin = self.save_locals();
64
65            unsafe {
66                (*self.function_stack.last_mut().unwrap()).loop_depth += 1;
67            }
68
69            let var = self.push_local(&varname);
70
71            let body = self.parse_block();
72
73            unsafe {
74                (*self.function_stack.last_mut().unwrap()).loop_depth -= 1;
75            }
76
77            self.restore_locals(locals_begin);
78
79            let end = self.lexer.current().location;
80
81            let has_end =
82                self.expect_match_end_and_consume(Type::ReservedEnd, &MatchLexeme::new(&match_do));
83            unsafe {
84                (*body).has_end = has_end;
85            }
86
87            let node = unsafe {
88                (*self.allocator).alloc(AstStatFor::new(
89                    Location::new(start.begin, end.end),
90                    var,
91                    from,
92                    to,
93                    step,
94                    body,
95                    has_do,
96                    match_do.location,
97                ))
98            };
99
100            if self.options.store_cst_data {
101                let cst_node = unsafe {
102                    (*self.allocator).alloc(CstStatFor::new(
103                        varname.colon_position,
104                        equals_position,
105                        end_comma_position,
106                        step_comma_position,
107                    ))
108                };
109                self.cst_node_map
110                    .try_insert(node as *mut AstNode, cst_node as *mut CstNode);
111            }
112
113            node as *mut AstStat
114        } else {
115            let mut names = TempVector::new(&mut self.scratch_binding);
116            let mut vars_comma_position: AstArray<Position> = AstArray {
117                data: core::ptr::null_mut(),
118                size: 0,
119            };
120            names.push_back(varname);
121
122            if self.lexer.current().r#type == Type(b',' as i32) {
123                if self.options.store_cst_data {
124                    let mut initial_comma_position = self.lexer.current().location.begin;
125                    self.next_lexeme();
126                    let _ = self.parse_binding_list(
127                        &mut names,
128                        false,
129                        &mut vars_comma_position,
130                        &mut initial_comma_position,
131                        core::ptr::null_mut(),
132                        false,
133                    );
134                } else {
135                    self.next_lexeme();
136                    let _ = self.parse_binding_list(
137                        &mut names,
138                        false,
139                        core::ptr::null_mut(),
140                        core::ptr::null_mut(),
141                        core::ptr::null_mut(),
142                        false,
143                    );
144                }
145            }
146
147            let in_location = self.lexer.current().location;
148            let has_in = self.expect_and_consume_type(Type::ReservedIn, "for loop");
149
150            let mut values = TempVector::new(&mut self.scratch_expr);
151            let mut values_comma_positions = TempVector::new(&mut self.scratch_position);
152            self.parse_expr_list(
153                &mut values,
154                if self.options.store_cst_data {
155                    Some(&mut values_comma_positions)
156                } else {
157                    None
158                },
159            );
160
161            let match_do = *self.lexer.current();
162            let has_do = self.expect_and_consume_type(Type::ReservedDo, "for loop");
163
164            let locals_begin = self.save_locals();
165
166            unsafe {
167                (*self.function_stack.last_mut().unwrap()).loop_depth += 1;
168            }
169
170            let mut vars = TempVector::new(&mut self.scratch_local);
171
172            for i in 0..names.size() {
173                let local = self.push_local(names.operator_index(i));
174                vars.push_back(local);
175            }
176
177            let body = self.parse_block();
178
179            unsafe {
180                (*self.function_stack.last_mut().unwrap()).loop_depth -= 1;
181            }
182
183            self.restore_locals(locals_begin);
184
185            let end = self.lexer.current().location;
186
187            let has_end =
188                self.expect_match_end_and_consume(Type::ReservedEnd, &MatchLexeme::new(&match_do));
189            unsafe {
190                (*body).has_end = has_end;
191            }
192
193            let vars_array = self.copy_temp_vector_t(&vars);
194            let values_array = self.copy_temp_vector_t(&values);
195
196            let node = unsafe {
197                (*self.allocator).alloc(AstStatForIn::new(
198                    Location::new(start.begin, end.end),
199                    vars_array,
200                    values_array,
201                    body,
202                    has_in,
203                    in_location,
204                    has_do,
205                    match_do.location,
206                ))
207            };
208
209            if self.options.store_cst_data {
210                let annotation = self.extract_annotation_colon_positions(&names);
211                let values_comma = self.copy_temp_vector_t(&values_comma_positions);
212                let cst_node = unsafe {
213                    (*self.allocator).alloc(CstStatForIn::new(
214                        annotation,
215                        vars_comma_position,
216                        values_comma,
217                    ))
218                };
219                self.cst_node_map
220                    .try_insert(node as *mut AstNode, cst_node as *mut CstNode);
221            }
222
223            node as *mut AstStat
224        }
225    }
226}