use super::{Cursor, Error, Literal, Root, SourcePos, State, Token, TokenKind, Transition};
#[derive(Debug)]
pub(super) struct ByteLiteral {
value: Option<u8>,
escaping: Option<(usize, SourcePos)>,
pos: SourcePos,
}
impl ByteLiteral {
pub fn at(cursor: &Cursor) -> Self {
Self { value: None, escaping: None, pos: cursor.pos() }
}
pub fn visit(mut self, cursor: &Cursor) -> Transition {
match (&self, cursor.peek()) {
(_, None) => Transition::error(Root, Error::unexpected_eof(cursor.pos())),
(&Self { value: Some(c), .. }, Some(b'\'')) => Transition::produce(
Root,
Token {
kind: TokenKind::Literal(Literal::Byte(c)),
pos: self.pos,
},
),
(&Self { value: Some(_), .. }, Some(c)) => {
Transition::error(self, Error::unexpected(c, cursor.pos()))
}
(&Self { escaping: Some((offset, pos)), .. }, Some(value)) => {
self.escaping = None;
if let Some(c) = validate_escape(value) {
self.value = Some(c);
Transition::step(self)
} else {
self.value = Some(b'\0');
let escape_sequence = &cursor.slice()[offset ..= cursor.offset()];
Transition::error(self, Error::invalid_escape_sequence(escape_sequence, pos))
}
}
(_, Some(b'\\')) => {
self.escaping = Some((cursor.offset(), cursor.pos()));
Transition::step(self)
}
(&Self { value: None, .. }, Some(b'\'')) => {
Transition::error(Root, Error::empty_byte_literal(self.pos))
}
(_, Some(value)) => {
self.value = Some(value);
Transition::step(self)
}
}
}
}
impl From<ByteLiteral> for State {
fn from(state: ByteLiteral) -> State {
Self::ByteLiteral(state)
}
}
#[derive(Debug)]
pub(super) struct StringLiteral {
value: Vec<u8>,
escaping: Option<(usize, SourcePos)>,
pos: SourcePos,
}
impl StringLiteral {
pub fn at(cursor: &Cursor) -> Self {
Self {
value: Vec::with_capacity(8), escaping: None,
pos: cursor.pos(),
}
}
pub fn visit(mut self, cursor: &Cursor) -> Transition {
match (&self, cursor.peek()) {
(_, None) => Transition::error(Root, Error::unexpected_eof(cursor.pos())),
(&Self { escaping: Some((offset, pos)), .. }, Some(value)) => {
self.escaping = None;
if let Some(c) = validate_escape(value) {
self.value.push(c);
Transition::step(self)
} else {
let escape_sequence = &cursor.slice()[offset ..= cursor.offset()];
Transition::error(self, Error::invalid_escape_sequence(escape_sequence, pos))
}
}
(_, Some(b'\\')) => {
self.escaping = Some((cursor.offset(), cursor.pos()));
Transition::step(self)
}
(_, Some(b'\"')) => Transition::produce(
Root,
Token {
kind: TokenKind::Literal(Literal::String(self.value.into_boxed_slice())),
pos: self.pos,
},
),
(_, Some(value)) => {
self.value.push(value);
Transition::step(self)
}
}
}
}
impl From<StringLiteral> for State {
fn from(state: StringLiteral) -> State {
Self::StringLiteral(state)
}
}
fn validate_escape(sequence: u8) -> Option<u8> {
match sequence {
b'"' => Some(b'"'),
b'\'' => Some(b'\''),
b'n' => Some(b'\n'),
b't' => Some(b'\t'),
b'0' => Some(b'\0'),
b'\\' => Some(b'\\'),
_ => None,
}
}