use crate::lexer::{Punct, TokKind, Token};
use crate::source::Span;
#[derive(Clone)]
pub struct Cursor<'a> {
toks: &'a [Token],
pos: usize,
}
impl<'a> Cursor<'a> {
pub fn new(toks: &'a [Token]) -> Cursor<'a> {
Cursor { toks, pos: 0 }
}
pub fn pos(&self) -> usize {
self.pos
}
pub fn set_pos(&mut self, pos: usize) {
self.pos = pos;
}
pub fn rest(&self) -> &'a [Token] {
&self.toks[self.pos.min(self.toks.len())..]
}
pub fn all(&self) -> &'a [Token] {
self.toks
}
pub fn peek(&self) -> Token {
self.nth(0)
}
pub fn nth(&self, n: usize) -> Token {
match self.toks.get(self.pos + n) {
Some(t) => *t,
None => Token {
kind: TokKind::Eof,
span: self.end_span(),
preceded_by_space: true,
},
}
}
fn end_span(&self) -> Span {
match self.toks.last() {
Some(t) => Span::new(t.span.hi, t.span.hi),
None => Span::DUMMY,
}
}
pub fn is_empty(&self) -> bool {
self.pos >= self.toks.len()
}
pub fn at_end(&self) -> bool {
matches!(self.peek().kind, TokKind::Eof | TokKind::Eol)
}
pub fn advance(&mut self) -> Token {
let t = self.peek();
if self.pos < self.toks.len() {
self.pos += 1;
}
t
}
pub fn eat_punct(&mut self, p: Punct) -> Option<Token> {
if self.peek().is_punct(p) {
Some(self.advance())
} else {
None
}
}
pub fn check_punct(&self, p: Punct) -> bool {
self.peek().is_punct(p)
}
pub fn remaining_span(&self) -> Span {
let rest = self.rest();
match (rest.first(), rest.last()) {
(Some(a), Some(b)) => a.span.to(b.span),
_ => self.end_span(),
}
}
pub fn split_commas(&self) -> Vec<&'a [Token]> {
let rest = self.rest();
let mut out = Vec::new();
let mut depth = 0i32;
let mut start = 0usize;
let mut saw_any = false;
for (i, t) in rest.iter().enumerate() {
match t.kind {
TokKind::Punct(Punct::LParen | Punct::LBracket | Punct::LBrace) => depth += 1,
TokKind::Punct(Punct::RParen | Punct::RBracket | Punct::RBrace) => depth -= 1,
TokKind::Punct(Punct::Comma) if depth <= 0 => {
out.push(&rest[start..i]);
start = i + 1;
saw_any = true;
continue;
}
_ => {}
}
saw_any = true;
}
if saw_any || start < rest.len() {
out.push(&rest[start..]);
}
out
}
}