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
78 if stop_at.matches(&token) {
79 return (!statements.is_empty() || last_statement.is_some()).then_some(Self {
80 statements,
81 last_statement,
82 });
83 }
84
85 loop {
86 if token.token_type == TokenType::EndOfFile {
87 break;
88 }
89 let mut failed_parsing = false;
90
91 if let Some(statement) = Statement::parse(token.clone(), lexer, errors) {
92 if last_statement.is_some() {
93 if let Ok(range) = statement.get_range() {
97 errors.push(ParseError::new(
98 range.start,
99 "Statements after a termination statement are not allowed.".to_string(),
100 Some(range.end),
101 ));
102 }
103 }
104
105 maybe_next_token!(lexer, maybe_semicolon, TokenType::Symbol(Symbol::Semicolon));
106 statements.push((Pointer::new(statement), maybe_semicolon))
107 } else if let Some(statement) =
108 TerminationStatement::parse(token.clone(), lexer, errors)
109 {
110 maybe_next_token!(lexer, maybe_semicolon, TokenType::Symbol(Symbol::Semicolon));
111 last_statement = Some((Pointer::new(statement), maybe_semicolon));
112 } else {
113 failed_parsing = true;
114 }
115
116 let state = lexer.save_state();
117 let next_token = lexer.next_token();
118
119 if stop_at.matches(&next_token) {
120 lexer.set_state(state);
121
122 break;
123 } else if token.token_type != TokenType::EndOfFile && failed_parsing {
124 errors.push(ParseError::new(
125 state.lexer_position(),
126 format!(
127 "Unexpected {}",
128 get_token_type_display_extended(&token.token_type)
129 ),
130 Some(state.lexer_position()),
131 ));
132 }
133
134 token = next_token;
135 }
136
137 (!statements.is_empty() || last_statement.is_some()).then_some(Self {
138 statements,
139 last_statement,
140 })
141 }
142}
143
144impl Block {
145 pub fn is_empty(&self) -> bool {
147 self.statements.is_empty() && self.last_statement.is_none()
148 }
149}
150
151fn get_range<T: GetRange>(
154 statement: &T,
155 semi_colon: &Option<Token>,
156) -> Result<Range, GetRangeError> {
157 let statement_range = statement.get_range();
158
159 if let Some(semicolon) = semi_colon {
160 Ok(Range::new(
161 statement_range?.start,
162 semicolon.get_range()?.end,
163 ))
164 } else {
165 statement_range
166 }
167}
168
169impl GetRange for Block {
170 fn get_range(&self) -> Result<Range, GetRangeError> {
171 if self.is_empty() {
172 return Err(GetRangeError::EmptyBlock);
173 }
174 if let Some((first_statement, semi_colon)) = self.statements.first() {
175 let last_statement_range = match &self.last_statement {
176 Some((statement, semi_colon)) => get_range(statement, semi_colon),
177 None => self
178 .statements
179 .first()
180 .map(|(statement, semi_colon)| get_range(statement, semi_colon))
181 .unwrap(), };
183
184 return Ok(Range::new(
185 get_range(first_statement, semi_colon)?.start,
186 last_statement_range?.end,
187 ));
188 }
189
190 match &self.last_statement {
191 Some((statement, semi_colon)) => get_range(statement, semi_colon),
192 None => Err(GetRangeError::EmptyBlock),
193 }
195 }
196}
197
198impl Print for Block {
199 fn print_final_trivia(&self) -> String {
200 if self.is_empty() {
201 String::new()
202 } else if let Some(last_statement) = self.last_statement.as_ref() {
203 last_statement.print_final_trivia()
204 } else {
205 self.statements.print_final_trivia()
206 }
207 }
208
209 fn print_without_final_trivia(&self) -> String {
210 if self.is_empty() {
211 String::new()
212 } else if self.statements.is_empty() {
213 self.last_statement
214 .as_ref()
215 .unwrap()
216 .print_without_final_trivia()
217 } else if self.last_statement.is_none() {
218 self.statements.print_without_final_trivia()
219 } else {
220 self.statements.print_without_final_trivia()
221 + &self
222 .last_statement
223 .as_ref()
224 .unwrap()
225 .print_without_final_trivia()
226 }
227 }
228}