use log::debug;
use std::error::Error;
use std::fmt;
use std::io;
#[derive(Debug)]
pub struct Pos {
pub line: usize,
pub column: usize,
}
impl Pos {
pub fn from(input: &[u8], offset: usize) -> Self {
let (mut line, mut column) = (1, 1);
for byte in &input[..offset] {
if *byte == b'\n' {
line += 1;
column = 1;
} else {
column += 1;
}
}
Self { line, column }
}
}
impl fmt::Display for Pos {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "line: {}, column: {}", self.line, self.column)
}
}
pub trait ScanError: Error + From<io::Error> + Sized {
fn position(&mut self, p: Pos);
}
type SplitResult<'input, TokenType, Error> =
Result<(Option<(&'input [u8], TokenType)>, usize), Error>;
pub trait Splitter: Sized {
type Error: ScanError;
type TokenType: std::fmt::Debug;
fn split<'input>(
&mut self,
data: &'input [u8],
) -> SplitResult<'input, Self::TokenType, Self::Error>;
}
pub struct Scanner<S: Splitter> {
offset: usize,
mark: usize,
splitter: S,
}
impl<S: Splitter> Scanner<S> {
pub fn new(splitter: S) -> Self {
Self {
offset: 0,
mark: 0,
splitter,
}
}
pub fn position(&self, input: &[u8]) -> Pos {
Pos::from(input, self.offset)
}
pub fn splitter(&self) -> &S {
&self.splitter
}
pub fn mark(&mut self) {
self.mark = self.offset;
}
pub fn reset_to_mark(&mut self) {
self.offset = self.mark;
}
pub fn reset(&mut self) {
self.offset = 0;
}
}
type ScanResult<'input, TokenType, Error> =
Result<(usize, Option<(&'input [u8], TokenType)>, usize), Error>;
impl<S: Splitter> Scanner<S> {
pub fn scan<'input>(
&mut self,
input: &'input [u8],
) -> ScanResult<'input, S::TokenType, S::Error> {
debug!(target: "scanner", "scan({})", Pos::from(input, self.offset));
loop {
if self.offset < input.len() {
let data = &input[self.offset..];
match self.splitter.split(data) {
Err(mut e) => {
e.position(Pos::from(input, self.offset));
return Err(e);
}
Ok((None, 0)) => {
}
Ok((None, amt)) => {
self.consume(data, amt);
continue;
}
Ok((tok, amt)) => {
let start = self.offset;
self.consume(data, amt);
debug!(target: "scanner", "scan(start: {}, tok: {:?}, offset: {})", start, tok, self.offset);
return Ok((start, tok, self.offset));
}
}
}
return Ok((self.offset, None, self.offset));
}
}
fn consume(&mut self, data: &[u8], amt: usize) {
debug!(target: "scanner", "consume({amt})");
debug_assert!(amt <= data.len());
self.offset += amt;
}
}
impl<S: Splitter> fmt::Debug for Scanner<S> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Scanner")
.field("offset", &self.offset)
.field("mark", &self.mark)
.finish()
}
}