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