use crate::error::RobinPathError;
use crate::lexer::{Token, TokenKind};
pub struct TokenStream {
tokens: Vec<Token>,
pos: usize,
}
impl TokenStream {
pub fn new(tokens: Vec<Token>) -> Self {
Self { tokens, pos: 0 }
}
pub fn current(&self) -> Option<&Token> {
self.tokens.get(self.pos)
}
pub fn peek(&self) -> Option<&Token> {
self.tokens.get(self.pos)
}
pub fn peek_offset(&self, offset: usize) -> Option<&Token> {
self.tokens.get(self.pos + offset)
}
pub fn next(&mut self) -> Option<&Token> {
let tok = self.tokens.get(self.pos);
if tok.is_some() {
self.pos += 1;
}
if self.pos > 0 {
self.tokens.get(self.pos - 1)
} else {
None
}
}
pub fn advance(&mut self) {
if self.pos < self.tokens.len() {
self.pos += 1;
}
}
pub fn check(&self, kind: TokenKind) -> bool {
self.current().is_some_and(|t| t.kind == kind)
}
pub fn check_text(&self, text: &str) -> bool {
self.current().is_some_and(|t| t.text == text)
}
pub fn check_keyword(&self, kw: &str) -> bool {
self.current()
.is_some_and(|t| t.kind == TokenKind::Keyword && t.text == kw)
}
pub fn match_kind(&mut self, kind: TokenKind) -> bool {
if self.check(kind) {
self.advance();
true
} else {
false
}
}
pub fn match_keyword(&mut self, kw: &str) -> bool {
if self.check_keyword(kw) {
self.advance();
true
} else {
false
}
}
pub fn expect(&mut self, kind: TokenKind) -> Result<Token, RobinPathError> {
if let Some(tok) = self.current() {
if tok.kind == kind {
let tok = tok.clone();
self.advance();
return Ok(tok);
}
let line = tok.line;
let col = tok.column;
return Err(RobinPathError::parse(
format!(
"Expected {:?}, got {:?} '{}'",
kind, tok.kind, tok.text
),
line,
col,
));
}
Err(RobinPathError::parse(
format!("Expected {:?}, got EOF", kind),
0,
0,
))
}
pub fn expect_keyword(&mut self, kw: &str) -> Result<Token, RobinPathError> {
if let Some(tok) = self.current() {
if tok.kind == TokenKind::Keyword && tok.text == kw {
let tok = tok.clone();
self.advance();
return Ok(tok);
}
let line = tok.line;
let col = tok.column;
return Err(RobinPathError::parse(
format!(
"Expected keyword '{}', got {:?} '{}'",
kw, tok.kind, tok.text
),
line,
col,
));
}
Err(RobinPathError::parse(
format!("Expected keyword '{}', got EOF", kw),
0,
0,
))
}
pub fn is_at_end(&self) -> bool {
self.pos >= self.tokens.len()
|| self.current().is_some_and(|t| t.kind == TokenKind::Eof)
}
pub fn save(&self) -> usize {
self.pos
}
pub fn restore(&mut self, pos: usize) {
self.pos = pos;
}
pub fn skip_newlines(&mut self) -> usize {
let mut count = 0;
while self.check(TokenKind::Newline) {
self.advance();
count += 1;
}
count
}
pub fn skip_comments_and_newlines(&mut self) {
while !self.is_at_end() {
if self.check(TokenKind::Newline) || self.check(TokenKind::Comment) {
self.advance();
} else {
break;
}
}
}
pub fn collect_line(&mut self) -> Vec<Token> {
let mut tokens = Vec::new();
while !self.is_at_end() {
if self.check(TokenKind::Newline) || self.check(TokenKind::Eof) {
break;
}
if let Some(tok) = self.current() {
if tok.is_continuation {
tokens.push(tok.clone());
self.advance();
continue;
}
tokens.push(tok.clone());
self.advance();
}
}
tokens
}
pub fn position(&self) -> usize {
self.pos
}
pub fn len(&self) -> usize {
self.tokens.len()
}
pub fn is_empty(&self) -> bool {
self.tokens.is_empty()
}
pub fn current_line(&self) -> usize {
self.current().map_or(0, |t| t.line)
}
pub fn current_column(&self) -> usize {
self.current().map_or(0, |t| t.column)
}
pub fn peek_past_newlines(&self) -> Option<&Token> {
let mut i = self.pos;
while i < self.tokens.len() {
let tok = &self.tokens[i];
if tok.kind != TokenKind::Newline && tok.kind != TokenKind::Comment {
return Some(tok);
}
i += 1;
}
None
}
}