use crate::error::LexError;
use crate::lexer::SpannedResult;
use logos::{Logos, Span};
use partiql_common::syntax::line_offset_tracker::LineOffsetTracker;
use partiql_common::syntax::location::ByteOffset;
type EmbeddedDocStringResult<'input> = SpannedResult<&'input str, ByteOffset, LexError<'input>>;
#[derive(Logos, Debug, Clone, PartialEq)]
#[logos(skip r#"[^/*'"`\r\n\u0085\u2028\u2029]+"#)] enum EmbeddedDocToken {
#[regex(r"(([\r])?[\n])|\u0085|\u2028|\u2029")]
Newline,
#[regex(r"`(``)*")]
Embed,
}
pub struct EmbeddedDocLexer<'input, 'tracker> {
lexer: logos::Lexer<'input, EmbeddedDocToken>,
tracker: &'tracker mut LineOffsetTracker,
}
impl<'input, 'tracker> EmbeddedDocLexer<'input, 'tracker> {
#[inline]
pub fn new(input: &'input str, tracker: &'tracker mut LineOffsetTracker) -> Self {
EmbeddedDocLexer {
lexer: EmbeddedDocToken::lexer(input),
tracker,
}
}
fn next_internal(&mut self) -> Option<EmbeddedDocStringResult<'input>> {
let next_token = self.lexer.next();
match next_token {
Some(Ok(EmbeddedDocToken::Embed)) => {
let Span {
start: b_start,
end: b_end,
} = self.lexer.span();
let start_quote_len = b_end - b_start;
loop {
let next_tok = self.lexer.next();
match next_tok {
Some(Ok(EmbeddedDocToken::Newline)) => {
self.tracker.record(self.lexer.span().end.into());
}
Some(Ok(EmbeddedDocToken::Embed)) => {
let Span {
start: e_start,
end: e_end,
} = self.lexer.span();
let end_quote_len = e_end - e_start;
if end_quote_len >= start_quote_len {
let backup = end_quote_len - start_quote_len;
let (str_start, str_end) =
(b_start + start_quote_len, e_end - end_quote_len);
let doc_value = &self.lexer.source()[str_start..str_end];
return Some(Ok((
b_start.into(),
doc_value,
(e_end - backup).into(),
)));
}
}
Some(_) => {
}
None => {
let Span { end, .. } = self.lexer.span();
return Some(Err((
b_start.into(),
LexError::UnterminatedDocLiteral,
end.into(),
)));
}
}
}
}
_ => None,
}
}
}
impl<'input> Iterator for EmbeddedDocLexer<'input, '_> {
type Item = EmbeddedDocStringResult<'input>;
#[inline(always)]
fn next(&mut self) -> Option<Self::Item> {
self.next_internal()
}
}