Skip to main content

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}