rl_lang/lexer/types/character_literal.rs
1//! Single-quoted character literal scanner.
2//!
3//! Handles `'x'` and simple escape sequences, emitting [`TokenType::CharacterLiteral`].
4use crate::lexer::{tokenizer::Tokenizer, tokentypes::TokenType};
5use crate::utils::errors::Error;
6
7impl Tokenizer {
8 /// Scans a single-quoted character literal and emits [`TokenType::CharacterLiteral`].
9 ///
10 /// Only a single character is allowed between the quotes.
11 /// Supports the following escape sequences:
12 ///
13 /// | Sequence | Meaning |
14 /// |----------|-----------------|
15 /// | `\n` | newline |
16 /// | `\t` | tab |
17 /// | `\r` | carriage return |
18 /// | `\0` | null |
19 /// | `\\` | backslash |
20 /// | '\"' | double quote |
21 /// | `\'` | single quote |
22 ///
23 /// # Errors
24 ///
25 /// - `unterminated character literal` -> if EOF is reached or no closing `'` is found
26 /// - `unknown escape sequence` -> if `\` is followed by an unrecognized character
27 pub fn character_literal(&mut self) -> Result<(), Error> {
28 self.advance();
29
30 if self.is_at_end() {
31 return Err(self.err("unterminated character literal", self.current_span()));
32 }
33
34 let character = self.source[self.current - 1];
35 let value: char = if character == '\\' {
36 // escape sequence
37 if self.is_at_end() {
38 return Err(self.err("unterminated character literal", self.current_span()));
39 }
40 let escaped = self.source[self.current];
41 self.advance();
42 match escaped {
43 'n' => '\n',
44 't' => '\t',
45 'r' => '\r',
46 '\\' => '\\',
47 '\"' => '\"',
48 '\'' => '\'',
49 '0' => '\0',
50 _ => {
51 return Err(self.err(
52 format!("unknown escape sequence `\\{}`", escaped),
53 self.current_span(),
54 ));
55 }
56 }
57 } else {
58 character
59 };
60
61 if self.peek() != '\'' {
62 return Err(self.err("unterminated character literal", self.current_span()));
63 }
64
65 self.advance();
66
67 self.add_token(TokenType::CharacterLiteral(value));
68 Ok(())
69 }
70}