use std::cell::Cell;
use sql_dialect_fmt_syntax::{keyword_kind_for, Dialect, SyntaxKind};
use crate::event::Event;
use crate::input::Input;
use crate::ParseError;
const INITIAL_FUEL: u32 = 1024;
pub(crate) use crate::contextual::ContextualKeyword;
pub(crate) struct Parser<'a> {
input: &'a Input<'a>,
dialect: Dialect,
pos: usize,
fuel: Cell<u32>,
exhausted_fuel_at: Cell<Option<usize>>,
events: Vec<Event>,
errors: Vec<ParseError>,
}
impl<'a> Parser<'a> {
pub(crate) fn new(input: &'a Input<'a>, dialect: Dialect) -> Self {
Parser {
input,
dialect,
pos: 0,
fuel: Cell::new(INITIAL_FUEL),
exhausted_fuel_at: Cell::new(None),
events: Vec::new(),
errors: Vec::new(),
}
}
pub(crate) fn dialect(&self) -> Dialect {
self.dialect
}
pub(crate) fn parse(mut self) -> (Vec<Event>, Vec<ParseError>) {
crate::grammar::source_file(&mut self);
if let Some(pos) = self.exhausted_fuel_at.get() {
self.errors.push(ParseError {
message: "parser fuel exhausted; recovered at current token".into(),
offset: self.input.offset(pos),
len: self.input.token_len(pos),
line_column: None,
});
}
(self.events, self.errors)
}
pub(crate) fn at_eof(&self) -> bool {
self.pos >= self.input.len()
}
pub(crate) fn pos(&self) -> usize {
self.pos
}
fn nth(&self, n: usize) -> SyntaxKind {
if self.pos + n >= self.input.len() {
return SyntaxKind::EOF;
}
debug_assert_ne!(
self.fuel.get(),
0,
"parser stuck — no progress at pos {}",
self.pos
);
if self.fuel.get() == 0 {
if self.exhausted_fuel_at.get().is_none() {
self.exhausted_fuel_at.set(Some(self.pos));
}
return SyntaxKind::EOF;
}
self.fuel.set(self.fuel.get() - 1);
self.input.kind(self.pos + n)
}
#[inline]
fn keyword_kind(&self, text: &str) -> Option<SyntaxKind> {
keyword_kind_for(text, self.dialect)
}
pub(crate) fn at(&self, kind: SyntaxKind) -> bool {
if kind.is_keyword() {
self.nth(0) == SyntaxKind::IDENT
&& self.keyword_kind(self.input.text(self.pos)) == Some(kind)
} else {
self.nth(0) == kind
}
}
pub(crate) fn at_name(&self) -> bool {
match self.nth(0) {
SyntaxKind::QUOTED_IDENT | SyntaxKind::PLACEHOLDER => true,
SyntaxKind::IDENT => self
.keyword_kind(self.input.text(self.pos))
.is_none_or(is_identifier_compatible_keyword),
_ => false,
}
}
pub(crate) fn at_ident_like(&self) -> bool {
matches!(self.nth(0), SyntaxKind::IDENT | SyntaxKind::QUOTED_IDENT)
}
pub(crate) fn at_keyword(&self) -> bool {
self.nth(0) == SyntaxKind::IDENT && self.keyword_kind(self.input.text(self.pos)).is_some()
}
pub(crate) fn nth_contextual(&self, n: usize, kw: ContextualKeyword) -> bool {
self.nth(n) == SyntaxKind::IDENT
&& self
.input
.text(self.pos + n)
.eq_ignore_ascii_case(kw.text())
}
pub(crate) fn nth_any_contextual(&self, n: usize, kws: &[ContextualKeyword]) -> bool {
kws.iter().any(|&kw| self.nth_contextual(n, kw))
}
pub(crate) fn nth_at(&self, n: usize, kind: SyntaxKind) -> bool {
if kind.is_keyword() {
self.nth(n) == SyntaxKind::IDENT
&& self.keyword_kind(self.input.text(self.pos + n)) == Some(kind)
} else {
self.nth(n) == kind
}
}
fn current_remapped(&self) -> SyntaxKind {
let raw = self.input.kind(self.pos);
if raw == SyntaxKind::IDENT {
self.keyword_kind(self.input.text(self.pos))
.unwrap_or(SyntaxKind::IDENT)
} else {
raw
}
}
fn advance(&mut self, kind: SyntaxKind) {
if self.at_eof() {
return;
}
self.events.push(Event::Advance { kind });
self.pos += 1;
self.fuel.set(INITIAL_FUEL);
}
pub(crate) fn bump_any(&mut self) {
let kind = self.current_remapped();
self.advance(kind);
}
pub(crate) fn bump_as(&mut self, kind: SyntaxKind) {
debug_assert!(!self.at_eof(), "bump_as past end of input");
self.advance(kind);
}
pub(crate) fn bump(&mut self, kind: SyntaxKind) {
debug_assert!(self.at(kind), "bump({kind:?}) but not at it");
self.advance(kind);
}
pub(crate) fn eat(&mut self, kind: SyntaxKind) -> bool {
if self.at(kind) {
self.advance(kind);
true
} else {
false
}
}
pub(crate) fn expect(&mut self, kind: SyntaxKind) {
if !self.eat(kind) {
self.error(format!("expected {}", kind.describe()));
}
}
pub(crate) fn error(&mut self, msg: impl Into<String>) {
let offset = self.input.offset(self.pos);
let len = self.input.token_len(self.pos);
self.errors.push(ParseError {
message: msg.into(),
offset,
len,
line_column: None,
});
}
pub(crate) fn err_and_bump(&mut self, msg: impl Into<String>) {
self.error(msg);
if self.at_eof() {
return;
}
let m = self.start();
self.bump_any();
m.complete(self, SyntaxKind::ERROR);
}
pub(crate) fn start(&mut self) -> Marker {
let index = self.events.len();
self.events.push(Event::Open {
kind: SyntaxKind::ERROR,
forward_parent: None,
});
Marker {
index,
completed: false,
}
}
}
fn is_identifier_compatible_keyword(kind: SyntaxKind) -> bool {
matches!(
kind,
SyntaxKind::LANGUAGE_KW
| SyntaxKind::JAVASCRIPT_KW
| SyntaxKind::PYTHON_KW
| SyntaxKind::JAVA_KW
| SyntaxKind::SCALA_KW
| SyntaxKind::SQL_KW
)
}
pub(crate) struct Marker {
index: usize,
completed: bool,
}
impl Marker {
pub(crate) fn complete(mut self, p: &mut Parser, kind: SyntaxKind) -> CompletedMarker {
self.completed = true;
p.events[self.index] = Event::Open {
kind,
forward_parent: None,
};
p.events.push(Event::Close);
CompletedMarker { index: self.index }
}
pub(crate) fn abandon(mut self, p: &mut Parser) {
self.completed = true;
p.events[self.index] = Event::Tombstone;
}
}
impl Drop for Marker {
fn drop(&mut self) {
if !self.completed && !std::thread::panicking() {
panic!("Marker dropped without being completed");
}
}
}
#[derive(Clone, Copy)]
pub(crate) struct CompletedMarker {
index: usize,
}
impl CompletedMarker {
pub(crate) fn precede(self, p: &mut Parser) -> Marker {
let new_index = p.events.len();
match &mut p.events[self.index] {
Event::Open { forward_parent, .. } => {
debug_assert!(
forward_parent.is_none(),
"node already has a forward parent"
);
*forward_parent = Some(new_index - self.index);
}
_ => debug_assert!(false, "precede must point at an open event"),
}
p.events.push(Event::Open {
kind: SyntaxKind::ERROR,
forward_parent: None,
});
Marker {
index: new_index,
completed: false,
}
}
}