orql 0.1.0

A toy SQL parser for a subset of the Oracle dialect.
Documentation
use super::{Error, MetaTracker, ParserInner, Result};
use crate::{
    ast::{Node, OrderBy, OrderDirection, OrderExpr, OrderNulls, OrderNullsPosition},
    parser::expression::ParseExprContext,
    scanner::{Keyword, Reserved, Token, TokenType},
};

impl<'s, M> ParserInner<'s, M>
where
    M: MetaTracker<'s>,
{
    /// Parses a required "ORDER BY" clause.
    pub(super) fn parse_order_by_required(&mut self) -> Result<OrderBy<'s, M::NodeId>> {
        let order_token = expect_token!(|t = self.next_token()| "the ORDER (BY) keyword" match {
            TokenType::Keyword(Keyword::ORDER) => Node((), self.meta_tracker.on_node_start(t.loc)),
        });
        self.parse_order_by_impl(order_token)
    }

    /// Parses an optional "ORDER BY" clause.
    pub(super) fn parse_order_by(&mut self) -> Result<Option<OrderBy<'s, M::NodeId>>> {
        // ~ no need for alias stealing: oracle won't parse `select * from
        // dual order` successfully anyway

        let order_token = if let Some(Token {
            ttype: TokenType::Keyword(Keyword::ORDER),
            loc,
        }) = self.peek_token()?
        {
            let loc = *loc;
            self.consume_token()?;
            Node((), self.meta_tracker.on_node_start(loc))
        } else {
            return Ok(None);
        };
        self.parse_order_by_impl(order_token).map(Some)
    }

    fn parse_order_by_impl(
        &mut self,
        order_token: Node<(), M::NodeId>,
    ) -> Result<OrderBy<'s, M::NodeId>> {
        let siblings_token = if let Some(Token {
            ttype: TokenType::Identifier(_, Some(Reserved::SIBLINGS)),
            loc,
        }) = self.peek_token()?
        {
            let loc = *loc;
            self.consume_token()?;
            Some(Node((), self.meta_tracker.on_node_start(loc)))
        } else {
            None
        };
        let by_token = expect_token!(|t = self.next_token()| "the BY keyword" match {
            TokenType::Keyword(Keyword::BY) => Node((), self.meta_tracker.on_node_start(t.loc)),
        });

        let mut expressions = vec![self.parse_order_expr()?];
        while let Some(Token {
            ttype: TokenType::Comma,
            ..
        }) = self.peek_token()?
        {
            self.consume_token()?;
            self.meta_tracker.on_node_end();
            expressions.push(self.parse_order_expr()?);
        }
        Ok(OrderBy {
            order_token,
            siblings_token,
            by_token,
            expressions,
        })
    }

    fn parse_order_expr(&mut self) -> Result<OrderExpr<'s, M::NodeId>> {
        let expr = self
            .expr_parser()
            .with_context(ParseExprContext::for_order_by())
            .parse()?;
        let direction = if let Some(Token {
            ttype: TokenType::Keyword(kw @ Keyword::ASC | kw @ Keyword::DESC),
            loc,
        }) = self.peek_token()?
        {
            let dir = if matches!(kw, Keyword::ASC) {
                OrderDirection::Asc
            } else {
                OrderDirection::Desc
            };
            let loc = *loc;
            self.consume_token()?;
            Some(Node(dir, self.meta_tracker.on_node_start(loc)))
        } else {
            None
        };
        let nulls = if let Some(Token {
            ttype: TokenType::Identifier(_, Some(Reserved::NULLS)),
            loc,
        }) = self.peek_token()?
        {
            let loc = *loc;
            self.consume_token()?;
            let nulls_token = Node((), self.meta_tracker.on_node_start(loc));
            let position = match self.next_token()? {
                Some(
                    ref t @ Token {
                        ttype: TokenType::Identifier(_, Some(reserved)),
                        loc,
                    },
                ) => {
                    let pos = match reserved {
                        Reserved::FIRST => OrderNullsPosition::First,
                        Reserved::LAST => OrderNullsPosition::Last,
                        _ => return Err(Error::unexpected_token(t, "the FIRST or LAST keyword")),
                    };
                    let pos_id = self.meta_tracker.on_node_start(loc);
                    Node(pos, pos_id)
                }
                Some(t) => return Err(Error::unexpected_token(t, "the FIRST or LAST keyword")),
                None => return unexpected_eof_err!(self, "the FIRST or LAST keyword"),
            };
            Some(OrderNulls {
                nulls_token,
                position,
            })
        } else {
            None
        };
        Ok(OrderExpr {
            expr,
            direction,
            nulls,
        })
    }
}