use unicase::UniCase;
use super::unicode::is_identifier_continue;
use crate::syn::error::{SyntaxError, bail, syntax_error};
use crate::syn::lexer::keywords::KEYWORDS;
use crate::syn::lexer::{BytesReader, Lexer};
use crate::syn::token::{Span, Token, TokenKind};
const BRACKET_CHARACTERS: [u8; 3] = const {
let mut b = [0; 3];
if '⟨'.encode_utf8(&mut b).len() != 3 {
panic!()
}
b
};
const BRACKET_START_CHARACTER: u8 = BRACKET_CHARACTERS[0];
impl Lexer<'_> {
pub fn unescape_ident_span<'a>(
str: &'a str,
span: Span,
buffer: &'a mut Vec<u8>,
) -> Result<&'a str, SyntaxError> {
let mut reader = BytesReader::new(str.as_bytes());
match reader.next() {
Some(b'`') => Self::unescape_backtick_span(reader, span, buffer),
Some(BRACKET_START_CHARACTER) => Self::unescape_bracket_span(reader, span, buffer),
_ => Ok(str),
}
}
fn unescape_backtick_span<'a>(
mut reader: BytesReader,
span: Span,
buffer: &'a mut Vec<u8>,
) -> Result<&'a str, SyntaxError> {
buffer.clear();
loop {
let before = reader.offset();
let x = reader.next().expect("lexer validated input");
match x {
b'\\' => {
Self::lex_common_escape_sequence(&mut reader, span, before, buffer, false)?;
}
b'`' => break,
x => {
buffer.push(x);
}
}
}
Ok(unsafe { std::str::from_utf8_unchecked(buffer) })
}
fn unescape_bracket_span<'a>(
mut reader: BytesReader,
span: Span,
buffer: &'a mut Vec<u8>,
) -> Result<&'a str, SyntaxError> {
buffer.clear();
assert_eq!(reader.complete_char(BRACKET_START_CHARACTER).expect("valid character"), '⟨');
loop {
let before = reader.offset();
let x = reader.next().expect("lexer validated input");
match x {
b'\\' => {
Self::lex_common_escape_sequence(&mut reader, span, before, buffer, false)?;
}
x if !x.is_ascii() => {
let c = reader.complete_char(x).expect("valid character");
if c == '⟩' {
break;
} else {
let mut char_buffer = [0u8; 4];
buffer.extend_from_slice(c.encode_utf8(&mut char_buffer).as_bytes());
}
}
x => {
buffer.push(x);
}
}
}
Ok(unsafe { std::str::from_utf8_unchecked(buffer) })
}
pub(super) fn lex_param(&mut self) -> Token {
loop {
if let Some(x) = self.reader.peek()
&& (x.is_ascii_alphanumeric() || x == b'_')
{
self.reader.next();
continue;
}
return self.finish_token(TokenKind::Parameter);
}
}
pub(super) fn lex_surrounded_param(&mut self, is_backtick: bool) -> Token {
match self.lex_surrounded_ident_err(is_backtick) {
Ok(_) => self.finish_token(TokenKind::Parameter),
Err(e) => self.invalid_token(e),
}
}
pub(super) fn lex_ident_from_next_byte(&mut self, start: u8) -> Token {
debug_assert!(matches!(start, b'a'..=b'z' | b'A'..=b'Z' | b'_'));
self.lex_ident()
}
pub(super) fn lex_ident(&mut self) -> Token {
loop {
if let Some(x) = self.reader.peek()
&& is_identifier_continue(x)
{
self.reader.next();
continue;
}
let str = self.span_str(self.current_span());
if let Some(x) = KEYWORDS.get(&UniCase::ascii(str)).copied() {
if x != TokenKind::Identifier {
return self.finish_token(x);
}
} else if str == "NaN" {
return self.finish_token(TokenKind::NaN);
} else if str == "Infinity" {
return self.finish_token(TokenKind::Infinity);
}
return self.finish_token(TokenKind::Identifier);
}
}
pub(super) fn lex_surrounded_ident(&mut self, is_backtick: bool) -> Token {
match self.lex_surrounded_ident_err(is_backtick) {
Ok(_) => self.finish_token(TokenKind::Identifier),
Err(e) => self.invalid_token(e),
}
}
pub(super) fn lex_surrounded_ident_err(
&mut self,
is_backtick: bool,
) -> Result<(), SyntaxError> {
let start_span = self.current_span();
loop {
let Some(x) = self.reader.next() else {
let end_char = if is_backtick {
'`'
} else {
'⟩'
};
let error = syntax_error!("Unexpected end of file, expected identifier to end with `{end_char}`", @self.current_span());
return Err(error);
};
match x {
b'`' if is_backtick => {
return Ok(());
}
b'\\' => {
let Some(next) = self.reader.next() else {
bail!("Unexpected end of file, expected identifier to end.", @start_span => "Identifier starting here.");
};
if !next.is_ascii() {
self.reader.complete_char(next)?;
}
}
BRACKET_START_CHARACTER if !is_backtick => {
if self.reader.complete_char(BRACKET_START_CHARACTER)? == '⟩' {
return Ok(());
}
}
x => {
if !x.is_ascii() {
self.reader.complete_char(x)?;
}
}
}
}
}
}