pub use crate::scanner::{CommentStyle, Location};
use crate::{
ast::{Hint, Ident, Identifier, Node, Statement, StatementType},
scanner::{Keyword, PeekableScanner, Scanner, Token, TokenType},
};
mod error;
pub use error::*;
mod meta;
pub use meta::{Comment, Id, Metadata};
use meta::{DefaultTracker, LocationsTracker, Tracker as MetaTracker, VoidTracker};
mod precedence;
use precedence::Prec;
mod iter;
pub use iter::*;
#[cfg(test)]
mod tests;
macro_rules! unexpected_eof_err {
($parser:expr, $expected:expr) => {
Err($crate::parser::Error::Unexpected {
unexpected: "end-of-file".into(),
expected: $expected,
loc: $parser.next_loc,
})
};
}
macro_rules! let_next_token {
($parser:expr, $bind:pat) => {
let Some($bind) = $parser.next_token()? else {
panic!("invalid token assumed");
};
};
}
macro_rules! expect_token {
(
|$token_var:ident| $expected:literal match {
$($ttype:pat $(if $cond:expr)? => $handler:expr $(,)?)*
}
) => {{
match $token_var.ttype {
$( $ttype $(if $cond)? => $handler, )*
#[allow(unreachable_patterns)]
_ => return Err($crate::parser::Error::unexpected_token($token_var, $expected)),
}
}};
(
|$token_var:ident = $parser:ident.$next_token:ident()| $expected:literal match {
$($ttype:pat $(if $cond:expr)? => $handler:expr $(,)?)*
}
) => {{
expect_token!(|$token_var = ($parser).$next_token()| $expected match {
$($ttype $(if $cond)? => $handler),*
})
}};
(
|$token_var:ident = ($parser:expr).$next_token:ident()| $expected:literal match {
$($ttype:pat $(if $cond:expr)? => $handler:expr $(,)?)*
}
) => {{
let $token_var = {
match $parser.$next_token()? {
None => return unexpected_eof_err!($parser, $expected),
Some(t) => t,
}
};
expect_token! {
|$token_var| $expected match {
$( $ttype $(if $cond)? => $handler )*
}
}
}};
}
macro_rules! expect_reserved {
(
|$token_var:ident $(= $parser:ident.$next_token:ident())?| $expected:literal match {
$($ttype:pat $(if $cond:expr)? => $handler:expr $(,)?)*
}
) => {{
expect_reserved!(|$token_var $(= ($parser).$next_token())? | $expected match {
$($ttype $(if $cond)? => $handler),*
})
}};
(
|$token_var:ident $(= ($parser:expr).$next_token:ident())?| $expected:literal match {
$($ttype:pat $(if $cond:expr)? => $handler:expr $(,)?)*
}
) => {{
$(
let $token_var = {
match $parser.$next_token()? {
None => return unexpected_eof_err!($parser, $expected),
Some(t) => t,
}
};
)?
expect_token! {
|$token_var| $expected match {
$crate::scanner::TokenType::Identifier(_, Some(_reserved)) => {
match _reserved {
$( $ttype $(if $cond)? => $handler, )*
#[allow(unreachable_patterns)]
_ => return Err($crate::parser::Error::unexpected_token($token_var, $expected)),
}
}
}
}
}};
}
mod condition;
mod expression;
mod order;
mod select;
mod window;
pub fn parse<'s>(s: &'s str) -> Result<Vec<Statement<'s, ()>>> {
parse_::<VoidTracker>(s).map(|(ast, _)| ast)
}
pub fn iter<'s>(s: &'s str) -> impl Iterator<Item = iter::IterItem<'s, ()>> {
iter_::<VoidTracker>(s)
}
pub fn parse_with_locations<'s>(s: &'s str) -> Result<Vec<Statement<'s, Location>>> {
parse_::<LocationsTracker>(s).map(|(ast, _)| ast)
}
pub fn iter_with_locations<'s>(s: &'s str) -> impl Iterator<Item = iter::IterItem<'s, Location>> {
iter_::<LocationsTracker>(s)
}
pub fn parse_with_metadata<'s>(
s: &'s str,
) -> Result<(Vec<Statement<'s, Id>>, impl Metadata<'s, NodeId = Id>)> {
parse_::<DefaultTracker>(s)
}
pub fn iter_with_metadata<'s>(s: &'s str) -> impl iter::Iter<'s, Id> {
iter_::<DefaultTracker>(s)
}
#[allow(clippy::type_complexity)]
fn parse_<'s, M: MetaTracker<'s> + Default>(
s: &'s str,
) -> Result<(Vec<Statement<'s, M::NodeId>>, M::Metadata)> {
let mut p = ParserInner::new(Scanner::new(s), M::default());
Ok((p.parse_statements()?, p.finish()?))
}
fn iter_<'s, M: MetaTracker<'s> + Default + 's>(
s: &'s str,
) -> impl iter::Iter<'s, M::NodeId, Item = iter::IterItem<'s, M::NodeId>> {
ParserInner::new(Scanner::new(s), M::default()).into_iter()
}
struct ParserInner<'s, M> {
tokens: PeekableScanner<'s>,
next_loc: Location,
nest_level: usize,
meta_tracker: M,
}
impl<'s, M> AsMut<Self> for ParserInner<'s, M> {
fn as_mut(&mut self) -> &mut Self {
self
}
}
impl<'s, M> ParserInner<'s, M>
where
M: MetaTracker<'s>,
{
fn new(scanner: Scanner<'s>, tracker: M) -> Self {
let tokens = scanner.peekable();
Self {
next_loc: tokens.inner().location(),
nest_level: 0,
tokens,
meta_tracker: tracker,
}
}
fn advance(&mut self) -> Result<Option<Token<'s>>> {
let t = self.tokens.next();
self.next_loc = self.tokens.location();
t.map_or(Ok(None), |t| t.map(Some).map_err(Error::from))
}
fn next_token(&mut self) -> Result<Option<Token<'s>>> {
while let Some(t) = self.advance()? {
if !self.meta_tracker.accept_comment(&t) {
return Ok(Some(t));
}
}
Ok(None)
}
fn skip_comments(&mut self) -> Result<(usize, Location)> {
let (mut pos, mut loc) = (self.tokens.position(), self.tokens.location());
loop {
match self.tokens.peek() {
None => break,
Some(Err(e)) => return Err(e.into()),
Some(Ok(t)) => {
if self.meta_tracker.accept_comment(t) {
pos = self.tokens.position();
loc = self.tokens.location();
self.advance()?;
} else {
break;
}
}
}
}
Ok((pos, loc))
}
fn peek_comment(&mut self) -> Result<Option<(&crate::scanner::Comment<'s>, &Location)>> {
match self.tokens.peek() {
Some(Err(e)) => Err(e.into()),
Some(Ok(Token {
ttype: TokenType::Comment(c),
loc,
})) => Ok(Some((c, loc))),
None | Some(Ok(_)) => Ok(None),
}
}
fn peek_token(&mut self) -> Result<Option<&Token<'s>>> {
loop {
match self.tokens.peek() {
None => return Ok(None),
Some(Err(e)) => return Err(e.into()),
Some(Ok(t)) => {
if self.meta_tracker.accept_comment(t) {
self.advance()?;
} else {
break;
}
}
}
}
self.tokens
.peek()
.map_or(Ok(None), |t| t.as_ref().map(Some).map_err(Error::from))
}
fn consume_token(&mut self) -> Result<()> {
self.next_token().map(|_| ())
}
fn finish(mut self) -> Result<M::Metadata> {
if let Some(t) = self.next_token()? {
return Err(Error::unexpected_token(t, "end-of-file"));
}
Ok(self.meta_tracker.finish())
}
fn parse_statements(&mut self) -> Result<Vec<Statement<'s, M::NodeId>>> {
let mut stmts = Vec::new();
while let Some(stmt) = self.parse_statement_()? {
stmts.push(stmt);
}
Ok(stmts)
}
fn parse_statement_(&mut self) -> Result<Option<Statement<'s, M::NodeId>>> {
let statement = match self.peek_token()? {
None => return Ok(None), Some(t) => match t.ttype {
TokenType::Semicolon => StatementType::Empty,
TokenType::Keyword(Keyword::SELECT)
| TokenType::Keyword(Keyword::WITH)
| TokenType::LeftParen => self
.parse_select()
.map(|s| StatementType::Select(s.into()))?,
_ => return Err(Error::unexpected_token(t, "a statement")), },
};
Ok(Some(Statement {
statement,
terminator: match self.next_token()? {
None => None,
Some(Token {
ttype: TokenType::Semicolon,
loc,
}) => Some(Node((), self.meta_tracker.on_node_start(loc))),
Some(t) => {
return Err(Error::unexpected_token(t, "a semicolon or end-of-file"));
}
},
}))
}
fn parse_hint(&mut self) -> Result<Option<Node<Hint<'s>, M::NodeId>>> {
match self.peek_comment()? {
Some((crate::scanner::Comment(text, style), loc))
if matches!(text.as_bytes(), [b'+', ..]) =>
{
let hint = Hint {
text: &text[1..],
comment_style: *style,
};
let loc = *loc;
self.advance()?;
Ok(Some(Node(hint, self.meta_tracker.on_node_start(loc))))
}
_ => Ok(None),
}
}
fn parse_identifier(&mut self) -> Result<Identifier<'s, M::NodeId>> {
let ident = self.parse_ident()?;
self.parse_identifier_(ident)
}
fn parse_identifier_(
&mut self,
init_ident: Node<Ident<'s>, M::NodeId>,
) -> Result<Identifier<'s, M::NodeId>> {
if matches!(
self.peek_token()?,
Some(Token {
ttype: TokenType::Dot,
..
})
) {
let mut parts = Vec::with_capacity(3);
parts.push(init_ident);
self.consume_token()?; loop {
self.meta_tracker.on_node_end();
expect_token! {
|t = self.next_token()| "an identifier" match {
TokenType::Identifier(ident, _) => {
parts.push(Node(ident, self.meta_tracker.on_node_start(t.loc)))
}
}
}
if let Some(t) = self.peek_token()?
&& matches!(t.ttype, TokenType::Dot)
{
continue;
} else {
break Ok(Identifier::Qualified(parts));
}
}
} else {
Ok(Identifier::Simple(init_ident))
}
}
fn parse_ident(&mut self) -> Result<Node<Ident<'s>, M::NodeId>> {
expect_token!(|t = self.next_token()| "a simple identifier" match {
TokenType::Identifier(ident, _) => Ok(Node(ident, self.meta_tracker.on_node_start(t.loc)))
})
}
}
fn parse_parens<'s, M, P, T, F>(parser: &mut P, f: F) -> Result<T>
where
P: AsMut<ParserInner<'s, M>>,
M: MetaTracker<'s>,
F: FnOnce(&mut P, M::NodeId) -> Result<T>,
{
let inner = parser.as_mut();
expect_token!(|t = inner.next_token()| "an opening parenthesis" match {
TokenType::LeftParen => parse_opened_parens(parser, t.into(), f)
})
}
enum OpenedParen<'s, ID> {
Token(Token<'s>),
NodeId(ID),
}
impl<'s, ID> From<Token<'s>> for OpenedParen<'s, ID> {
fn from(value: Token<'s>) -> Self {
Self::Token(value)
}
}
fn parse_opened_parens<'s, M, P, T, F>(
parser: &mut P,
opened_paren: OpenedParen<'s, M::NodeId>,
f: F,
) -> Result<T>
where
P: AsMut<ParserInner<'s, M>>,
M: MetaTracker<'s>,
F: FnOnce(&mut P, M::NodeId) -> Result<T>,
{
let inner = parser.as_mut();
let node_id = match opened_paren {
OpenedParen::Token(token) => inner.meta_tracker.on_node_start(token.loc),
OpenedParen::NodeId(id) => id,
};
inner.meta_tracker.on_node_end();
inner.nest_level += 1;
let result = f(parser, node_id)?;
let inner = parser.as_mut();
expect_token! {
|t = inner.next_token()| "a closing parenthesis" match {
TokenType::RightParen => {}
}
}
inner.meta_tracker.on_node_end_restore(node_id);
inner.nest_level -= 1;
Ok(result)
}
fn parse_comma_separated<'s, M, P, T, F>(parser: &mut P, mut f: F) -> Result<Vec<T>>
where
P: AsMut<ParserInner<'s, M>>,
M: MetaTracker<'s>,
F: FnMut(&mut P) -> Result<T>,
{
let initial = f(parser)?;
parse_comma_separated_rest(parser, initial, f)
}
fn parse_comma_separated_rest<'s, M, P, T, F>(
parser: &mut P,
initial: T,
mut f: F,
) -> Result<Vec<T>>
where
P: AsMut<ParserInner<'s, M>>,
M: MetaTracker<'s>,
F: FnMut(&mut P) -> Result<T>,
{
let mut results = vec![initial];
loop {
let as_mut = parser.as_mut();
if let Some(Token {
ttype: TokenType::Comma,
..
}) = as_mut.peek_token()?
{
as_mut.meta_tracker.on_node_end();
as_mut.consume_token()?;
results.push(f(parser)?);
} else {
break;
}
}
Ok(results)
}