use crate::error::Result;
use crate::intern::StringInterner;
use crate::source::{FileRegistry, SourceLocation};
use crate::token::{Token, TokenKind};
pub trait TokenSource {
fn next_token(&mut self) -> Result<Token>;
fn unget_token(&mut self, token: Token);
fn interner(&self) -> &StringInterner;
fn interner_mut(&mut self) -> &mut StringInterner;
fn files(&self) -> &FileRegistry;
fn is_file_in_target(&self, file_id: crate::source::FileId) -> bool {
let _ = file_id;
false
}
}
pub struct TokenSlice {
tokens: Vec<Token>,
pos: usize,
interner: StringInterner,
files: FileRegistry,
eof_loc: SourceLocation,
lookahead: Vec<Token>,
}
impl TokenSlice {
pub fn new(tokens: Vec<Token>, interner: StringInterner, files: FileRegistry) -> Self {
let eof_loc = tokens.last()
.map(|t| t.loc.clone())
.unwrap_or_default();
Self {
tokens,
pos: 0,
interner,
files,
eof_loc,
lookahead: Vec::new(),
}
}
pub fn position(&self) -> usize {
self.pos
}
pub fn remaining(&self) -> usize {
self.tokens.len().saturating_sub(self.pos)
}
}
impl TokenSource for TokenSlice {
fn next_token(&mut self) -> Result<Token> {
if let Some(token) = self.lookahead.pop() {
return Ok(token);
}
if self.pos < self.tokens.len() {
let token = self.tokens[self.pos].clone();
self.pos += 1;
Ok(token)
} else {
Ok(Token::new(TokenKind::Eof, self.eof_loc.clone()))
}
}
fn unget_token(&mut self, token: Token) {
self.lookahead.push(token);
}
fn interner(&self) -> &StringInterner {
&self.interner
}
fn interner_mut(&mut self) -> &mut StringInterner {
&mut self.interner
}
fn files(&self) -> &FileRegistry {
&self.files
}
}
pub struct TokenSliceRef<'a> {
tokens: Vec<Token>,
pos: usize,
interner: &'a StringInterner,
files: &'a FileRegistry,
eof_loc: SourceLocation,
lookahead: Vec<Token>,
}
impl<'a> TokenSliceRef<'a> {
pub fn new(
tokens: Vec<Token>,
interner: &'a StringInterner,
files: &'a FileRegistry,
) -> Self {
let eof_loc = tokens.last()
.map(|t| t.loc.clone())
.unwrap_or_default();
Self {
tokens,
pos: 0,
interner,
files,
eof_loc,
lookahead: Vec::new(),
}
}
pub fn next_token(&mut self) -> Result<Token> {
if let Some(token) = self.lookahead.pop() {
return Ok(token);
}
if self.pos < self.tokens.len() {
let token = self.tokens[self.pos].clone();
self.pos += 1;
Ok(token)
} else {
Ok(Token::new(TokenKind::Eof, self.eof_loc.clone()))
}
}
pub fn interner(&self) -> &StringInterner {
self.interner
}
pub fn files(&self) -> &FileRegistry {
self.files
}
}
impl<'a> TokenSource for TokenSliceRef<'a> {
fn next_token(&mut self) -> Result<Token> {
if let Some(token) = self.lookahead.pop() {
return Ok(token);
}
if self.pos < self.tokens.len() {
let token = self.tokens[self.pos].clone();
self.pos += 1;
Ok(token)
} else {
Ok(Token::new(TokenKind::Eof, self.eof_loc.clone()))
}
}
fn unget_token(&mut self, token: Token) {
self.lookahead.push(token);
}
fn interner(&self) -> &StringInterner {
self.interner
}
fn interner_mut(&mut self) -> &mut StringInterner {
panic!("interner_mut() called on TokenSliceRef - use from_source_with_typedefs()")
}
fn files(&self) -> &FileRegistry {
self.files
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::path::PathBuf;
#[test]
fn test_token_slice_empty() {
let interner = StringInterner::new();
let mut files = FileRegistry::new();
files.register(PathBuf::from("test.c"));
let mut slice = TokenSlice::new(vec![], interner, files);
let token = slice.next_token().unwrap();
assert!(matches!(token.kind, TokenKind::Eof));
}
#[test]
fn test_token_slice_tokens() {
let mut interner = StringInterner::new();
let mut files = FileRegistry::new();
let file_id = files.register(PathBuf::from("test.c"));
let loc = SourceLocation::new(file_id, 1, 1);
let foo = interner.intern("foo");
let tokens = vec![
Token::new(TokenKind::Ident(foo), loc.clone()),
Token::new(TokenKind::Plus, loc.clone()),
Token::new(TokenKind::IntLit(42), loc.clone()),
];
let mut slice = TokenSlice::new(tokens, interner, files);
assert_eq!(slice.remaining(), 3);
let t1 = slice.next_token().unwrap();
assert!(matches!(t1.kind, TokenKind::Ident(_)));
let t2 = slice.next_token().unwrap();
assert!(matches!(t2.kind, TokenKind::Plus));
let t3 = slice.next_token().unwrap();
assert!(matches!(t3.kind, TokenKind::IntLit(42)));
let t4 = slice.next_token().unwrap();
assert!(matches!(t4.kind, TokenKind::Eof));
assert_eq!(slice.remaining(), 0);
}
}