use std::marker::PhantomData;
use crate::{FmtContext, PosnInCharStream};
use crate::{LexerError, LexerOfStr};
#[derive(Debug)]
pub struct LexerOfString<P, T, E>
where
P: PosnInCharStream,
{
text: String,
line_start_ncolumns: Vec<(P, usize)>,
_phantom_token: PhantomData<T>,
_phantom_error: PhantomData<E>,
}
impl<P, T, E> Default for LexerOfString<P, T, E>
where
P: PosnInCharStream,
{
fn default() -> Self {
Self {
text: String::new(),
line_start_ncolumns: Vec::new(),
_phantom_token: PhantomData,
_phantom_error: PhantomData,
}
}
}
impl<P, T, E> LexerOfString<P, T, E>
where
P: PosnInCharStream,
T: std::fmt::Debug + Clone,
E: LexerError<P>,
{
pub fn set_text<S: Into<String>>(mut self, text: S) -> Self {
self.text = text.into();
self.find_line_starts();
self
}
pub fn take_text(&mut self) -> String {
self.line_start_ncolumns.clear();
std::mem::take(&mut self.text)
}
pub fn text(&self) -> &str {
&self.text
}
pub fn lexer(&self) -> LexerOfStr<P, T, E> {
LexerOfStr::new(&self.text)
}
fn find_line_starts(&mut self) {
let mut line_start_ncolumns = Vec::new();
let mut s: &str = &self.text;
let mut pos = P::default();
line_start_ncolumns.push((pos, 0)); while let Some((line, next_line)) = s.split_once('\n') {
let ncolumns = line.chars().count();
line_start_ncolumns.push((pos, ncolumns));
pos = pos.advance_line(line.len() + 1);
s = next_line;
}
let ncolumns = s.chars().count();
line_start_ncolumns.push((pos, ncolumns));
self.line_start_ncolumns = line_start_ncolumns;
}
}
impl<P, T, E> FmtContext<P> for LexerOfString<P, T, E>
where
P: PosnInCharStream,
E: LexerError<P>,
{
fn line_length(&self, line: usize) -> usize {
self.line_start_ncolumns[line].1
}
fn fmt_line(&self, f: &mut dyn std::fmt::Write, line: usize) -> std::fmt::Result {
let s = &self.text[self.line_start_ncolumns[line].0.byte_ofs()..];
let s = s.split_once('\n').map(|(s, _)| s).unwrap_or(s);
write!(f, "{}", s)
}
}