use itertools::Itertools;
use std::collections::VecDeque;
use crate::tokenizer::{Token, Tokenizer};
pub struct TokenStream<'src> {
tokenizer: Tokenizer<'src>,
tokens: VecDeque<Token>,
bookmarks: VecDeque<usize>,
current_token: usize,
purged_tokens: usize,
}
impl<'src> TokenStream<'src> {
pub fn new(tokenizer: Tokenizer<'src>) -> Self {
Self {
tokenizer,
tokens: VecDeque::new(),
bookmarks: VecDeque::new(),
current_token: 0,
purged_tokens: 0,
}
}
#[inline]
pub fn source(&self) -> &'src [u8] {
self.tokenizer.source()
}
pub fn current_token_index(&self) -> usize {
self.current_token
}
pub fn next_token(&mut self) -> Option<Token> {
self.fetch_tokens(self.current_token);
let token = self.tokens.get(self.rel_pos(self.current_token)).cloned();
if token.is_some() {
self.bump();
}
token
}
pub fn peek_token(&mut self, n: usize) -> Option<&Token> {
self.fetch_tokens(self.current_token + n);
let token = self.tokens.get(self.rel_pos(self.current_token + n));
token
}
#[inline]
pub fn has_more(&mut self) -> bool {
self.peek_token(0).is_some()
}
pub fn bookmark(&mut self) -> Bookmark {
match self.bookmarks.back() {
None => {
self.bookmarks.push_back(self.current_token);
}
Some(rightmost) if *rightmost <= self.current_token => {
self.bookmarks.push_back(self.current_token)
}
Some(_) => {
self.bookmarks.push_back(self.current_token);
self.bookmarks.make_contiguous().sort();
}
}
Bookmark(self.current_token)
}
#[inline]
pub fn restore_bookmark(&mut self, bookmark: &Bookmark) {
self.current_token = bookmark.0;
}
pub fn remove_bookmark(&mut self, bookmark: Bookmark) {
match self.bookmarks.iter().find_position(|b| **b == bookmark.0) {
Some((pos, _)) => {
self.bookmarks.remove(pos);
}
None => {
panic!("trying to remove a non-existing bookmark");
}
}
}
#[inline]
pub fn enter_hex_pattern_mode(&mut self) {
self.tokenizer.enter_hex_pattern_mode()
}
#[inline]
pub fn enter_hex_jump_mode(&mut self) {
self.tokenizer.enter_hex_jump_mode()
}
}
impl<'src> TokenStream<'src> {
fn fetch_tokens(&mut self, abs_pos: usize) {
while self.rel_pos(abs_pos) >= self.tokens.len() {
match self.tokenizer.next_token() {
Some(token) => self.tokens.push_back(token),
None => break,
}
}
}
#[inline]
fn bump(&mut self) {
self.current_token += 1;
self.purge();
}
fn purge(&mut self) {
let n = if let Some(leftmost_bookmark) = self.bookmarks.front() {
assert!(*leftmost_bookmark >= self.purged_tokens);
self.rel_pos(*leftmost_bookmark)
} else {
self.rel_pos(self.current_token)
};
for _ in 0..n {
self.tokens.pop_front();
}
self.purged_tokens += n;
}
fn rel_pos(&self, abs_pos: usize) -> usize {
abs_pos - self.purged_tokens
}
}
pub struct Bookmark(usize);
#[cfg(test)]
mod test {
use crate::parser::token_stream::TokenStream;
use crate::tokenizer::{Token, Tokenizer};
use crate::Span;
#[test]
fn next() {
let mut t = TokenStream::new(Tokenizer::new(b"uno dos tres"));
assert_eq!(t.current_token, 0);
assert_eq!(t.next_token(), Some(Token::IDENT(Span(0..3))));
assert_eq!(t.current_token, 1);
assert_eq!(t.next_token(), Some(Token::WHITESPACE(Span(3..4))));
assert_eq!(t.current_token, 2);
assert_eq!(t.next_token(), Some(Token::IDENT(Span(4..7))));
assert_eq!(t.current_token, 3);
assert_eq!(t.next_token(), Some(Token::WHITESPACE(Span(7..8))));
assert_eq!(t.current_token, 4);
assert_eq!(t.next_token(), Some(Token::IDENT(Span(8..12))));
assert_eq!(t.current_token, 5);
assert_eq!(t.next_token(), None);
assert_eq!(t.current_token, 5);
assert_eq!(t.next_token(), None);
assert_eq!(t.current_token, 5);
}
#[test]
fn peek_and_bump() {
let mut t = TokenStream::new(Tokenizer::new(b"uno dos tres"));
assert_eq!(t.peek_token(0), Some(&Token::IDENT(Span(0..3))));
assert_eq!(t.peek_token(1), Some(&Token::WHITESPACE(Span(3..4))));
t.bump();
assert_eq!(t.peek_token(0), Some(&Token::WHITESPACE(Span(3..4))));
t.bump();
assert_eq!(t.peek_token(0), Some(&Token::IDENT(Span(4..7))));
t.bump();
assert_eq!(t.peek_token(0), Some(&Token::WHITESPACE(Span(7..8))));
t.bump();
assert_eq!(t.peek_token(0), Some(&Token::IDENT(Span(8..12))));
t.bump();
assert_eq!(t.peek_token(0), None);
t.bump();
assert_eq!(t.peek_token(0), None);
}
#[test]
fn bookmarks_1() {
let mut t = TokenStream::new(Tokenizer::new(b"uno dos tres"));
let b = t.bookmark();
assert_eq!(t.next_token(), Some(Token::IDENT(Span(0..3))));
assert_eq!(t.next_token(), Some(Token::WHITESPACE(Span(3..4))));
assert_eq!(t.next_token(), Some(Token::IDENT(Span(4..7))));
assert_eq!(t.next_token(), Some(Token::WHITESPACE(Span(7..8))));
assert_eq!(t.next_token(), Some(Token::IDENT(Span(8..12))));
assert_eq!(t.next_token(), None);
t.restore_bookmark(&b);
assert_eq!(t.next_token(), Some(Token::IDENT(Span(0..3))));
assert_eq!(t.next_token(), Some(Token::WHITESPACE(Span(3..4))));
assert_eq!(t.next_token(), Some(Token::IDENT(Span(4..7))));
assert_eq!(t.next_token(), Some(Token::WHITESPACE(Span(7..8))));
assert_eq!(t.next_token(), Some(Token::IDENT(Span(8..12))));
assert_eq!(t.next_token(), None);
t.restore_bookmark(&b);
t.remove_bookmark(b);
assert_eq!(t.next_token(), Some(Token::IDENT(Span(0..3))));
assert_eq!(t.next_token(), Some(Token::WHITESPACE(Span(3..4))));
assert_eq!(t.next_token(), Some(Token::IDENT(Span(4..7))));
assert_eq!(t.next_token(), Some(Token::WHITESPACE(Span(7..8))));
assert_eq!(t.next_token(), Some(Token::IDENT(Span(8..12))));
assert_eq!(t.next_token(), None);
}
#[test]
fn bookmarks_2() {
let mut t = TokenStream::new(Tokenizer::new(b"uno dos tres"));
assert_eq!(t.next_token(), Some(Token::IDENT(Span(0..3))));
let b1 = t.bookmark();
assert_eq!(t.next_token(), Some(Token::WHITESPACE(Span(3..4))));
assert_eq!(t.next_token(), Some(Token::IDENT(Span(4..7))));
t.restore_bookmark(&b1);
assert_eq!(t.next_token(), Some(Token::WHITESPACE(Span(3..4))));
let b2 = t.bookmark();
assert_eq!(t.next_token(), Some(Token::IDENT(Span(4..7))));
assert_eq!(t.next_token(), Some(Token::WHITESPACE(Span(7..8))));
assert_eq!(t.next_token(), Some(Token::IDENT(Span(8..12))));
t.restore_bookmark(&b2);
t.remove_bookmark(b2);
assert_eq!(t.next_token(), Some(Token::IDENT(Span(4..7))));
assert_eq!(t.next_token(), Some(Token::WHITESPACE(Span(7..8))));
assert_eq!(t.next_token(), Some(Token::IDENT(Span(8..12))));
assert_eq!(t.next_token(), None);
t.restore_bookmark(&b1);
assert_eq!(t.next_token(), Some(Token::WHITESPACE(Span(3..4))));
assert_eq!(t.next_token(), Some(Token::IDENT(Span(4..7))));
assert_eq!(t.next_token(), Some(Token::WHITESPACE(Span(7..8))));
assert_eq!(t.next_token(), Some(Token::IDENT(Span(8..12))));
assert_eq!(t.next_token(), None);
}
#[test]
fn bookmarks_3() {
let mut t = TokenStream::new(Tokenizer::new(b"uno dos tres"));
assert_eq!(t.next_token(), Some(Token::IDENT(Span(0..3))));
let b1 = t.bookmark();
let b2 = t.bookmark();
let b3 = t.bookmark();
assert_eq!(t.next_token(), Some(Token::WHITESPACE(Span(3..4))));
assert_eq!(t.next_token(), Some(Token::IDENT(Span(4..7))));
t.remove_bookmark(b3);
t.restore_bookmark(&b2);
assert_eq!(t.next_token(), Some(Token::WHITESPACE(Span(3..4))));
assert_eq!(t.next_token(), Some(Token::IDENT(Span(4..7))));
t.remove_bookmark(b2);
t.restore_bookmark(&b1);
assert_eq!(t.next_token(), Some(Token::WHITESPACE(Span(3..4))));
assert_eq!(t.next_token(), Some(Token::IDENT(Span(4..7))));
t.remove_bookmark(b1);
}
}