luau_parser/impl/block/
impl.rs1use lsp_types::Range;
4use luau_lexer::prelude::{Error, Lexer, Symbol, Token, TokenType};
5
6use crate::{
7 types::{
8 Block, GetRange, GetRangeError, Parse, ParseWithArgs, Pointer, Print, Statement,
9 TerminationStatement,
10 },
11 utils::get_token_type_display_extended,
12};
13
14trait MatchesToken {
16 fn matches(&self, token: &Token) -> bool;
18}
19
20impl<T: MatchesToken> MatchesToken for Option<T> {
21 #[inline]
22 fn matches(&self, token: &Token) -> bool {
23 self.as_ref().is_some_and(|value| value.matches(token))
24 }
25}
26
27impl MatchesToken for TokenType {
28 #[inline]
29 fn matches(&self, token: &Token) -> bool {
30 token == self
31 }
32}
33impl MatchesToken for Token {
34 #[inline]
35 fn matches(&self, token: &Token) -> bool {
36 token == self
37 }
38}
39
40impl MatchesToken for Vec<TokenType> {
41 #[inline]
42 fn matches(&self, token: &Token) -> bool {
43 self.contains(&token.token_type)
44 }
45}
46impl MatchesToken for Vec<Token> {
47 #[inline]
48 fn matches(&self, token: &Token) -> bool {
49 self.contains(token)
50 }
51}
52
53impl<const T: usize> MatchesToken for [Token; T] {
54 #[inline]
55 fn matches(&self, token: &Token) -> bool {
56 self.contains(token)
57 }
58}
59impl<const T: usize> MatchesToken for [TokenType; T] {
60 #[inline]
61 fn matches(&self, token: &Token) -> bool {
62 self.contains(&token.token_type)
63 }
64}
65
66impl<T: MatchesToken> ParseWithArgs<T> for Block {
67 fn parse_with(
68 mut token: Token,
69 lexer: &mut Lexer,
70 errors: &mut Vec<Error>,
71 stop_at: T,
72 ) -> Option<Self> {
73 let mut statements = Vec::new();
74 let mut last_statement = None;
75 let mut is_done = false;
76
77 if stop_at.matches(&token) {
78 return (!statements.is_empty() || last_statement.is_some()).then_some(Self {
79 statements,
80 last_statement,
81 });
82 }
83
84 loop {
85 if token.token_type == TokenType::EndOfFile {
86 is_done = true;
87 }
88 let mut failed_parsing = false;
89
90 if let Some(statement) = Statement::parse(token.clone(), lexer, errors) {
91 if last_statement.is_some() {
92 if let Ok(range) = statement.get_range() {
96 errors.push(Error::new(
97 range.start,
98 "Statements after a termination statement are not allowed.".to_string(),
99 Some(range.end),
100 ));
101 }
102 }
103
104 maybe_next_token!(lexer, maybe_semicolon, TokenType::Symbol(Symbol::Semicolon));
105 statements.push((Pointer::new(statement), maybe_semicolon))
106 } else if let Some(statement) =
107 TerminationStatement::parse(token.clone(), lexer, errors)
108 {
109 maybe_next_token!(lexer, maybe_semicolon, TokenType::Symbol(Symbol::Semicolon));
110 last_statement = Some((Pointer::new(statement), maybe_semicolon));
111 } else {
112 failed_parsing = true;
113 }
114
115 if is_done {
116 break;
117 }
118
119 let state = lexer.save_state();
120 let next_token = lexer.next_token();
121
122 if stop_at.matches(&next_token) {
123 lexer.set_state(state);
124
125 break;
126 } else if failed_parsing {
127 errors.push(Error::new(
128 state.lexer_position(),
129 format!(
130 "Unexpected {}",
131 get_token_type_display_extended(&token.token_type)
132 ),
133 Some(state.lexer_position()),
134 ));
135 }
136
137 token = next_token;
138 }
139
140 (!statements.is_empty() || last_statement.is_some()).then_some(Self {
141 statements,
142 last_statement,
143 })
144 }
145}
146
147impl Block {
148 pub const fn is_empty(&self) -> bool {
150 self.statements.is_empty() && self.last_statement.is_none()
151 }
152}
153
154fn get_range<T: GetRange>(
157 statement: &T,
158 semi_colon: &Option<Token>,
159) -> Result<Range, GetRangeError> {
160 let statement_range = statement.get_range();
161
162 if let Some(semicolon) = semi_colon {
163 Ok(Range::new(
164 statement_range?.start,
165 semicolon.get_range()?.end,
166 ))
167 } else {
168 statement_range
169 }
170}
171
172impl GetRange for Block {
173 fn get_range(&self) -> Result<Range, GetRangeError> {
174 if self.is_empty() {
175 return Err(GetRangeError::EmptyBlock);
176 }
177 if let Some((first_statement, semi_colon)) = self.statements.first() {
178 let last_statement_range = match &self.last_statement {
179 Some((statement, semi_colon)) => get_range(statement, semi_colon),
180 None => get_range(first_statement, semi_colon),
181 };
182
183 return Ok(Range::new(
184 get_range(first_statement, semi_colon)?.start,
185 last_statement_range?.end,
186 ));
187 }
188
189 match &self.last_statement {
190 Some((statement, semi_colon)) => get_range(statement, semi_colon),
191 None => Err(GetRangeError::EmptyBlock),
192 }
194 }
195}
196
197impl Print for Block {
198 fn print_final_trivia(&self) -> String {
199 if self.is_empty() {
200 String::new()
201 } else if let Some(last_statement) = self.last_statement.as_ref() {
202 last_statement.print_final_trivia()
203 } else {
204 self.statements.print_final_trivia()
205 }
206 }
207
208 fn print_without_final_trivia(&self) -> String {
209 if self.is_empty() {
210 String::new()
211 } else if self.statements.is_empty() {
212 #[allow(clippy::unwrap_used)]
215 self.last_statement
216 .as_ref()
217 .unwrap()
218 .print_without_final_trivia()
219 } else if self.last_statement.is_none() {
220 self.statements.print_without_final_trivia()
221 } else {
222 #[allow(clippy::unwrap_used)]
224 {
225 self.statements.print_without_final_trivia()
226 + &self
227 .last_statement
228 .as_ref()
229 .unwrap()
230 .print_without_final_trivia()
231 }
232 }
233 }
234}