1use smol_str::SmolStr;
4
5use crate::{
6 prelude::{Lexable, Lexer, ParseError},
7 utils::is_numeric,
8};
9
10#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
14#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
15pub enum LuauString {
16 SingleQuotes(SmolStr),
20
21 DoubleQuotes(SmolStr),
25
26 Backticks(SmolStr),
30
31 MultiLine(SmolStr),
41}
42
43impl LuauString {
44 fn count_back_slashes(characters: &[char]) -> usize {
46 if characters.is_empty() {
47 return 0;
48 }
49
50 let mut count = 0;
51 let mut i = characters.len() - 1;
52
53 while characters[i] == '\\' {
54 count += 1;
55
56 match i.checked_sub(1) {
57 Some(new_i) => i = new_i,
58 None => break,
59 }
60 }
61
62 count
63 }
64
65 fn is_escaped(characters: &[char]) -> bool {
67 if characters.len() < 2 {
68 false
69 } else {
70 Self::count_back_slashes(&characters[..characters.len() - 1]) % 2 != 0
71 }
72 }
73
74 #[inline]
76 fn is_multi_line_escaped(characters: &[char]) -> bool {
77 characters[characters.len() - 1] == '\n' || (Self::is_escaped(characters) )
83 }
84
85 fn parse_inner(lexer: &mut Lexer, quote_character: char) -> SmolStr {
91 let mut characters = vec![quote_character];
92 let start = lexer.lexer_position;
93 let mut is_done = false;
94
95 lexer.increment_position_by_char(quote_character);
96
97 while let Some(character) = lexer.current_char() {
98 if (character == '\n' || character == '\r') && !Self::is_multi_line_escaped(&characters)
99 {
100 lexer.errors.push(ParseError::new(
101 start,
102 format!(
103 "Strings must be single line, use `\\z` or `\\` here or add a {}.",
104 quote_character
105 ),
106 Some(lexer.lexer_position),
107 ));
108
109 break;
110 }
111
112 characters.push(character);
113 lexer.increment_position_by_char(character);
114
115 if character == quote_character && !Self::is_escaped(&characters) {
116 is_done = true;
117
118 break;
119 }
120 }
121
122 if !is_done {
123 lexer.errors.push(ParseError::new(
124 start,
125 format!("Missing {} to close string.", quote_character),
126 Some(lexer.lexer_position),
127 ));
128 }
129
130 SmolStr::from_iter(characters)
131 }
132
133 pub(crate) fn parse_multi_line(lexer: &mut Lexer) -> SmolStr {
135 let mut characters = vec!['['];
136 let start = lexer.lexer_position;
137 let mut equals_count = 0;
138 let mut is_done = false;
139
140 lexer.consume('[');
141 while lexer.consume('=') {
142 equals_count += 1;
143 characters.push('=');
144 }
145
146 if lexer.consume('[') {
147 characters.push('[');
148 } else {
149 lexer.errors.push(ParseError::new(
150 start,
151 "Missing `[`.".to_string(),
152 Some(lexer.lexer_position),
153 ));
154 }
155
156 while let Some(character) = lexer.current_char() {
157 characters.push(character);
158 lexer.increment_position_by_char(character);
159
160 if character == ']' && !Self::is_escaped(&characters) {
161 let mut matched_equals = true;
162
163 for _ in 0..equals_count {
164 if let Some(character) = lexer.current_char() {
165 characters.push(character);
166 lexer.increment_position_by_char(character);
167
168 matched_equals = character == '=';
169 }
170 }
171
172 if matched_equals && lexer.consume(']') {
173 characters.push(']');
174 is_done = true;
175
176 break;
177 }
178 }
179 }
180
181 if !is_done {
182 lexer.errors.push(ParseError::new(
183 start,
184 "Malformed multi-line string.".to_string(),
185 Some(lexer.lexer_position),
186 ));
187 }
188
189 SmolStr::from_iter(characters)
190 }
191}
192
193impl Lexable for LuauString {
194 fn try_lex(lexer: &mut Lexer) -> Option<Self> {
195 match lexer.current_char()? {
196 '"' => Some(Self::DoubleQuotes(Self::parse_inner(lexer, '"'))),
197 '\'' => Some(Self::SingleQuotes(Self::parse_inner(lexer, '\''))),
198 '`' => Some(Self::Backticks(Self::parse_inner(lexer, '`'))),
199 '[' => Some(Self::MultiLine(Self::parse_multi_line(lexer))),
200 _ => unreachable!("Invalid quote type."),
201 }
202 }
203}
204
205#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
209#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
210pub enum LuauNumber {
211 Plain(SmolStr),
217
218 Binary(SmolStr),
222
223 Hex(SmolStr),
227}
228
229impl LuauNumber {
230 fn parse_number_inner(lexer: &mut Lexer) -> Option<Self> {
232 let start = lexer.position;
233 let mut found_decimal = false;
234
235 loop {
236 let Some(current_char) = lexer.current_char() else {
237 break;
238 };
239
240 if is_numeric(current_char) {
241 lexer.increment_position_by_char(current_char);
242 } else if current_char == '.' {
243 if found_decimal {
244 break lexer.errors.push(ParseError::new(
245 lexer.lexer_position,
246 "Numbers can only have one decimal point.".to_string(),
247 None,
248 ));
249 }
250
251 lexer.increment_position_by_char(current_char);
252 found_decimal = true;
253 } else {
254 break;
255 }
256 }
257
258 Some(Self::Plain(SmolStr::from_iter(
259 lexer.chars[start..lexer.position].to_vec(),
260 )))
261 }
262
263 fn parse_hex_number(lexer: &mut Lexer) -> Option<Self> {
265 let start = lexer.position;
266 let mut found_digit = false;
267 let mut is_faulty = false;
268
269 lexer.consume('0');
270 lexer.consume('x');
271
272 loop {
273 let Some(current_char) = lexer.current_char() else {
274 break;
275 };
276
277 if current_char.is_ascii_hexdigit() {
278 lexer.increment_position_by_char(current_char);
279 found_digit = true;
280 } else {
281 break is_faulty = !current_char.is_whitespace();
282 }
283 }
284
285 if !found_digit {
287 lexer.errors.push(ParseError::new(
288 lexer.lexer_position,
289 "Hexadecimal numbers must have at least one digit after '0x'.".to_string(),
290 None,
291 ));
292 }
293 if found_digit && is_faulty {
294 lexer.errors.push(ParseError::new(
295 lexer.lexer_position,
296 "Hexadecimal numbers must only contain hexadecimal digits.".to_string(),
297 None,
298 ));
299 }
300
301 Some(Self::Hex(SmolStr::from_iter(
302 lexer.chars[start..lexer.position].to_vec(),
303 )))
304 }
305
306 fn parse_binary_number(lexer: &mut Lexer) -> Option<Self> {
308 let start = lexer.position;
309 let mut found_digit = false;
310 let mut is_faulty = false;
311
312 lexer.consume('0');
313 lexer.consume('b');
314
315 loop {
316 let Some(current_char) = lexer.current_char() else {
317 break;
318 };
319
320 if current_char == '0' || current_char == '1' {
321 lexer.increment_position_by_char(current_char);
322 found_digit = true;
323 } else {
324 break is_faulty = !current_char.is_whitespace();
325 }
326 }
327
328 if !found_digit {
330 lexer.errors.push(ParseError::new(
331 lexer.lexer_position,
332 "Binary number must have at least one digit after '0b'.".to_string(),
333 None,
334 ));
335 }
336 if found_digit && is_faulty {
337 lexer.errors.push(ParseError::new(
338 lexer.lexer_position,
339 "Binary number must only have 1s and 0s.".to_string(),
340 None,
341 ));
342 }
343
344 Some(Self::Binary(SmolStr::from_iter(
345 lexer.chars[start..lexer.position].to_vec(),
346 )))
347 }
348}
349
350impl Lexable for LuauNumber {
351 fn try_lex(lexer: &mut Lexer) -> Option<Self> {
352 match (lexer.current_char()?, lexer.next_char()) {
353 ('0', Some('b')) => Self::parse_binary_number(lexer),
354 ('0', Some('x')) => Self::parse_hex_number(lexer),
355 _ => Self::parse_number_inner(lexer),
356 }
357 }
358}
359
360#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
362#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
363pub enum Literal {
364 Number(LuauNumber),
366
367 String(LuauString),
369
370 Boolean(bool),
372}
373
374impl Literal {
375 #[inline]
377 pub fn parse_number(lexer: &mut Lexer) -> Option<Self> {
378 LuauNumber::try_lex(lexer).map(Self::Number)
379 }
380
381 #[inline]
383 pub fn parse_string(lexer: &mut Lexer) -> Option<Self> {
384 LuauString::try_lex(lexer).map(Self::String)
385 }
386}
387
388impl Lexable for Literal {
389 fn try_lex(_: &mut Lexer) -> Option<Self> {
393 panic!(
394 "\
395 `Literal::try_lex()` should never be used. \
396 Please read the documentation for this function."
397 )
398 }
399}
400
401impl_from!(Literal <= {
402 Number(LuauNumber),
403 String(LuauString),
404 Boolean(bool),
405});