use crate::error::{Result, WarningSet};
use crate::location::Span;
use std::fs::File;
use std::io::prelude::*;
use std::path::Path;
use std::rc::Rc;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn string_stream() {
let string = "What a nice content,\nall in a single stream!";
let origin = Path::new("somewhere");
let mut stream = StringStream::new(origin, string);
assert_eq!(stream.peek(), &*string);
for chr in string.chars() {
let got_char = stream.get();
match got_char {
Char::Char(c) => assert_eq!(chr, c),
Char::EOF => {
panic!("Found EOF in stream, while expecting {}", chr)
}
}
stream.incr_pos();
}
assert!(matches!(stream.get(), Char::EOF));
}
#[test]
fn unicode() {
let string = "До́брый день.";
let origin = Path::new("Russia");
let mut stream = StringStream::new(origin, string);
assert_eq!(stream.peek(), &*string);
let mut curr_pos = 0;
for chr in string.chars() {
match stream.get() {
Char::Char(c) => assert_eq!(chr, c),
Char::EOF => {
panic!("Found EOF in stream, while expecting {}", chr)
}
}
assert_eq!(&string[curr_pos..], stream.peek());
stream.incr_pos();
curr_pos += chr.len_utf8();
}
}
}
pub type StreamObject<T> = (T, Span);
pub trait Stream<'a> {
type Output: 'a;
fn get_at(&'a self, pos: usize) -> Option<StreamObject<Self::Output>>;
fn pos(&self) -> usize;
fn set_pos(&mut self, pos: usize);
fn pos_pp(&mut self) {
self.set_pos(self.pos() + 1);
}
fn pos_inc(&mut self, off: usize) {
self.set_pos(self.pos() + off);
}
fn get(&'a self) -> Option<StreamObject<Self::Output>> {
self.get_at(self.pos())
}
fn get_loc_of(&'a self, pos: usize) -> Option<Span> {
self.get_at(pos).map(|(_, location)| location)
}
fn next(&'a mut self) -> Option<StreamObject<Self::Output>> {
let pos = self.pos();
self.pos_pp();
self.get_at(pos)
}
}
#[derive(Debug)]
pub enum Char {
Char(char),
EOF,
}
pub struct StringStream {
origin: Rc<Path>,
spans: Vec<((usize, usize), usize)>,
stream: Rc<str>,
bytes_pos: usize,
chars_pos: usize,
length: usize,
eof_span: Span,
}
impl StringStream {
pub fn new(
origin: impl Into<Rc<Path>>,
string: impl Into<Rc<str>>,
) -> Self {
let origin = origin.into();
let string = string.into();
let mut current_char = 0;
let mut current_line = 0;
let mut spans = Vec::new();
for chr in string.chars() {
let start_pos = (current_line, current_char);
spans.push((start_pos, chr.len_utf8()));
if chr == '\n' {
current_line += 1;
current_char = 0;
} else {
current_char += 1;
}
}
Self {
origin: origin.clone(),
length: spans.len(),
stream: string,
spans,
bytes_pos: 0,
chars_pos: 0,
eof_span: Span::new(
origin,
(current_line, current_char),
(current_line, current_char),
),
}
}
pub fn from_file(file: impl Into<Rc<Path>>) -> Result<Self> {
let file = file.into();
let mut file_stream = File::open(file.as_ref())?;
let mut stream_buffer = String::new();
file_stream.read_to_string(&mut stream_buffer)?;
Ok(WarningSet::empty_with(StringStream::new(
file,
stream_buffer,
)))
}
pub fn pos(&self) -> usize {
self.chars_pos
}
pub fn continues(&self, keyword: &str) -> bool {
self.peek().starts_with(keyword)
}
pub fn shift(&mut self, length: usize) {
for _ in 0..length {
self.incr_pos();
}
}
pub fn incr_pos(&mut self) {
self.bytes_pos += self.spans[self.chars_pos].1;
self.chars_pos += 1;
}
pub fn decr_pos(&mut self) {
self.chars_pos -= 1;
self.bytes_pos -= self.spans[self.chars_pos].1;
}
pub fn peek(&self) -> &str {
&self.stream[self.bytes_pos..]
}
pub fn get(&self) -> Char {
self.peek()
.chars()
.next()
.map(Char::Char)
.unwrap_or(Char::EOF)
}
pub fn origin(&self) -> Rc<Path> {
self.origin.clone()
}
pub fn len(&self) -> usize {
self.length
}
pub fn is_empty(&self) -> bool {
self.chars_pos == self.length
}
pub fn curr_span(&self) -> Span {
if self.chars_pos == self.spans.len() {
self.eof_span.clone()
} else {
let (line, column) = self.spans[self.chars_pos].0;
Span::new(self.origin.clone(), (line, column), (line, column + 1))
}
}
pub fn span_between(&self, start: usize, end: usize) -> Span {
let start_location = self
.spans
.get(start)
.map(|x| x.0)
.unwrap_or_else(|| self.eof_span.start());
let end_location = self
.spans
.get(end)
.map(|x| x.0)
.unwrap_or_else(|| self.eof_span.end());
Span::new(self.origin.clone(), start_location, end_location)
}
}
impl std::fmt::Debug for StringStream {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.peek().fmt(f)
}
}