use rigsql_core::{Token, TokenKind};
#[derive(Debug, Clone)]
pub struct ParseDiagnostic {
pub offset: u32,
pub message: String,
}
pub struct ParseContext<'a> {
tokens: &'a [Token],
pos: usize,
source: &'a str,
diagnostics: Vec<ParseDiagnostic>,
}
impl<'a> ParseContext<'a> {
pub fn new(tokens: &'a [Token], source: &'a str) -> Self {
Self {
tokens,
pos: 0,
source,
diagnostics: Vec::new(),
}
}
pub fn source(&self) -> &'a str {
self.source
}
pub fn record_error(&mut self, message: &str) {
let offset = self
.peek()
.map(|t| t.span.start)
.unwrap_or_else(|| self.source.len() as u32);
self.diagnostics.push(ParseDiagnostic {
offset,
message: message.to_string(),
});
}
pub fn record_error_at(&mut self, offset: u32, message: &str) {
self.diagnostics.push(ParseDiagnostic {
offset,
message: message.to_string(),
});
}
pub fn take_diagnostics(&mut self) -> Vec<ParseDiagnostic> {
std::mem::take(&mut self.diagnostics)
}
pub fn pos(&self) -> usize {
self.pos
}
pub fn save(&self) -> usize {
self.pos
}
pub fn restore(&mut self, pos: usize) {
self.pos = pos;
}
pub fn peek(&self) -> Option<&'a Token> {
self.tokens.get(self.pos)
}
pub fn peek_kind(&self) -> Option<TokenKind> {
self.peek().map(|t| t.kind)
}
pub fn peek_non_trivia(&self) -> Option<&'a Token> {
let mut i = self.pos;
while i < self.tokens.len() {
if !self.tokens[i].kind.is_trivia() {
return Some(&self.tokens[i]);
}
i += 1;
}
None
}
pub fn peek_keyword(&self, kw: &str) -> bool {
self.peek_non_trivia()
.is_some_and(|t| t.kind == TokenKind::Word && t.text.eq_ignore_ascii_case(kw))
}
pub fn peek_keywords(&self, kws: &[&str]) -> bool {
let mut i = self.pos;
for kw in kws {
while i < self.tokens.len() && self.tokens[i].kind.is_trivia() {
i += 1;
}
if i >= self.tokens.len() {
return false;
}
let t = &self.tokens[i];
if t.kind != TokenKind::Word || !t.text.eq_ignore_ascii_case(kw) {
return false;
}
i += 1;
}
true
}
pub fn advance(&mut self) -> Option<&'a Token> {
if self.pos < self.tokens.len() {
let token = &self.tokens[self.pos];
self.pos += 1;
Some(token)
} else {
None
}
}
pub fn eat_trivia(&mut self) -> Vec<&'a Token> {
let mut trivia = Vec::new();
while self.pos < self.tokens.len() && self.tokens[self.pos].kind.is_trivia() {
trivia.push(&self.tokens[self.pos]);
self.pos += 1;
}
trivia
}
pub fn eat_keyword(&mut self, kw: &str) -> Option<&'a Token> {
if self.pos < self.tokens.len()
&& self.tokens[self.pos].kind == TokenKind::Word
&& self.tokens[self.pos].text.eq_ignore_ascii_case(kw)
{
let token = &self.tokens[self.pos];
self.pos += 1;
Some(token)
} else {
None
}
}
pub fn eat_kind(&mut self, kind: TokenKind) -> Option<&'a Token> {
if self.pos < self.tokens.len() && self.tokens[self.pos].kind == kind {
let token = &self.tokens[self.pos];
self.pos += 1;
Some(token)
} else {
None
}
}
pub fn at_eof(&self) -> bool {
self.pos >= self.tokens.len() || self.tokens[self.pos].kind == TokenKind::Eof
}
pub fn remaining(&self) -> &'a [Token] {
&self.tokens[self.pos..]
}
}