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 = 256;
macro_rules! contextual_keywords {
($(
$(#[$meta:meta])*
$variant:ident => $text:literal,
)*) => {
#[derive(Clone, Copy, PartialEq, Eq)]
pub(crate) enum ContextualKeyword {
$(
$(#[$meta])*
$variant,
)*
}
impl ContextualKeyword {
fn text(self) -> &'static str {
match self {
$(
ContextualKeyword::$variant => $text,
)*
}
}
}
};
}
contextual_keywords! {
At => "at",
Before => "before",
Asof => "asof",
MatchCondition => "match_condition",
MatchRecognize => "match_recognize",
Grouping => "grouping",
Sets => "sets",
Measures => "measures",
Pattern => "pattern",
Define => "define",
Subset => "subset",
Match => "match",
One => "one",
Skip => "skip",
Past => "past",
Next => "next",
To => "to",
NoCycle => "nocycle",
Changes => "changes",
Comment => "comment",
Version => "version",
Timestamp => "timestamp",
Of => "of",
Location => "location",
Tblproperties => "tblproperties",
Options => "options",
Partitioned => "partitioned",
Transaction => "transaction",
Work => "work",
Reverse => "reverse",
Default => "default",
Break => "break",
Continue => "continue",
Schema => "schema",
Database => "database",
Stage => "stage",
Sequence => "sequence",
Stream => "stream",
Dynamic => "dynamic",
Semantic => "semantic",
File => "file",
Format => "format",
Masking => "masking",
Policy => "policy",
Access => "access",
Tag => "tag",
AllowedValues => "allowed_values",
Propagate => "propagate",
ExemptOtherPolicies => "exempt_other_policies",
Tables => "tables",
Relationships => "relationships",
Facts => "facts",
Dimensions => "dimensions",
Metrics => "metrics",
Public => "public",
Private => "private",
Synonyms => "synonyms",
Labels => "labels",
AiSqlGeneration => "ai_sql_generation",
AiQuestionCategorization => "ai_question_categorization",
AiVerifiedQueries => "ai_verified_queries",
Question => "question",
VerifiedAt => "verified_at",
OnboardingQuestion => "onboarding_question",
VerifiedBy => "verified_by",
Role => "role",
User => "user",
Share => "share",
Cascade => "cascade",
Restrict => "restrict",
Option => "option",
Privileges => "privileges",
Materialized => "materialized",
Local => "local",
Global => "global",
Cluster => "cluster",
Clone => "clone",
Primary => "primary",
Key => "key",
Unique => "unique",
Foreign => "foreign",
References => "references",
Constraint => "constraint",
Check => "check",
Collate => "collate",
Vacuum => "vacuum",
Retain => "retain",
Hours => "hours",
Dry => "dry",
Run => "run",
Optimize => "optimize",
Zorder => "zorder",
Cache => "cache",
Lazy => "lazy",
Uncache => "uncache",
Refresh => "refresh",
History => "history",
Source => "source",
Target => "target",
}
pub(crate) struct Parser<'a> {
input: &'a Input<'a>,
dialect: Dialect,
pos: usize,
fuel: Cell<u32>,
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),
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);
(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;
}
assert_ne!(
self.fuel.get(),
0,
"parser stuck — no progress at pos {}",
self.pos
);
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 => 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_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,
});
}
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,
});
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 };
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 {
p.events.insert(
self.index,
Event::Open {
kind: SyntaxKind::ERROR,
},
);
Marker {
index: self.index,
completed: false,
}
}
}