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