use std::sync::LazyLock;
use rustc_hash::FxHashSet;
use super::ast::*;
use super::error::{ParseError, ParseErrors};
use super::lexer::Lexer;
use super::precedence::Precedence;
use super::token::{Position, Token, TokenType};
static RESERVED_KEYWORDS: LazyLock<FxHashSet<&'static str>> = LazyLock::new(|| {
[
"SELECT",
"FROM",
"WHERE",
"AND",
"OR",
"NOT",
"INSERT",
"INTO",
"VALUES",
"UPDATE",
"SET",
"DELETE",
"CREATE",
"DROP",
"TABLE",
"INDEX",
"VIEW",
"EXTENSION",
"PLANNER",
"SUPPORT",
"ALTER",
"ADD",
"PRIMARY",
"KEY",
"FOREIGN",
"REFERENCES",
"NULL",
"TRUE",
"FALSE",
"AS",
"ON",
"JOIN",
"INNER",
"OUTER",
"FULL",
"CROSS",
"GROUP",
"BY",
"ORDER",
"HAVING",
"LIMIT",
"OFFSET",
"UNION",
"INTERSECT",
"EXCEPT",
"CASE",
"WHEN",
"THEN",
"ELSE",
"END",
"DISTINCT",
"ALL",
"EXISTS",
"IN",
"BETWEEN",
"LIKE",
"GLOB",
"REGEXP",
"RLIKE",
"IS",
"ASC",
"DESC",
"NULLS",
"BEGIN",
"COMMIT",
"ROLLBACK",
"SAVEPOINT",
"RELEASE",
"IF",
"WITH",
"RECURSIVE",
]
.into_iter()
.collect()
});
const MAX_EXPRESSION_NESTING: usize = 128;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum PositionalParameterStyle {
Anonymous,
Explicit,
}
pub struct Parser {
pub(crate) source: Box<str>,
lexer: Lexer,
pub(crate) cur_token: Token,
pub(crate) peek_token: Token,
errors: Vec<ParseError>,
pub(crate) current_clause: String,
parameter_counter: usize,
pub(crate) positional_parameter_style: Option<PositionalParameterStyle>,
pub(crate) expression_depth: usize,
pub(crate) procedural_definition_depth: usize,
pub(crate) token_count: usize,
}
impl Parser {
fn next_parser_token(lexer: &mut Lexer) -> Token {
loop {
let token = lexer.next_token();
if token.token_type != TokenType::Comment {
return token;
}
}
}
pub fn new(input: &str) -> Self {
let normalized = if input.contains('\r') {
input.replace("\r\n", "\n").replace('\r', "\n")
} else {
input.to_owned()
};
let mut lexer = Lexer::new(&normalized);
let cur_token = Self::next_parser_token(&mut lexer);
let peek_token = Self::next_parser_token(&mut lexer);
Parser {
source: normalized.into(),
lexer,
cur_token,
peek_token,
errors: Vec::new(),
current_clause: String::new(),
parameter_counter: 1,
positional_parameter_style: None,
expression_depth: 0,
procedural_definition_depth: 0,
token_count: 2,
}
}
pub fn parse_program(&mut self) -> Result<Program, ParseErrors> {
let mut statements = Vec::with_capacity(1);
while !self.cur_token_is(TokenType::Eof) {
if self.cur_token_is(TokenType::Comment) {
self.next_token();
continue;
}
if let Some(stmt) = self.parse_statement() {
statements.push(stmt);
}
if self.peek_token_is_punctuator(";") {
while self.peek_token_is_punctuator(";") {
self.next_token();
}
self.next_token();
} else if self.peek_token_is(TokenType::Eof) {
self.next_token();
} else {
self.add_error(format!(
"expected ';' between statements before {}",
Self::format_token_for_error(&self.peek_token)
));
break;
}
self.parameter_counter = 1;
self.positional_parameter_style = None;
}
if !self.errors.is_empty() {
return Err(ParseErrors::from_errors_with_sql(
self.errors.clone(),
self.source.as_ref(),
));
}
Ok(Program { statements })
}
pub(crate) fn next_token(&mut self) {
let next = Self::next_parser_token(&mut self.lexer);
self.cur_token = std::mem::replace(&mut self.peek_token, next);
self.token_count = self.token_count.saturating_add(1);
if self.procedural_definition_depth > 0
&& self.cur_token.token_type == TokenType::Parameter
&& !self.cur_token.literal.starts_with(':')
{
self.add_error_at(
"stored procedural source cannot contain external '$n' or '?' parameters"
.to_string(),
self.cur_token.position,
);
}
}
pub(crate) fn cur_token_is(&self, t: TokenType) -> bool {
self.cur_token.token_type == t
}
pub(crate) fn peek_token_is(&self, t: TokenType) -> bool {
self.peek_token.token_type == t
}
pub(crate) fn cur_token_is_identifier_like(&self) -> bool {
match self.cur_token.token_type {
TokenType::Identifier => true,
TokenType::Keyword => {
!Self::is_reserved_keyword(&self.cur_token.literal)
}
_ => false,
}
}
pub(crate) fn cur_token_as_column_identifier(&self) -> Identifier {
Identifier::new(self.cur_token.clone(), self.cur_token.literal.clone())
}
pub(crate) fn parse_relation_identifier_current(&mut self) -> Option<Identifier> {
if !matches!(
self.cur_token.token_type,
TokenType::Identifier | TokenType::Keyword
) {
self.add_error(format!(
"expected relation name, got {}",
Self::format_token_for_error(&self.cur_token)
));
return None;
}
let token = self.cur_token.clone();
let mut value = self.cur_token.literal.clone();
while self.peek_token_is_punctuator(".") {
self.next_token();
if !self.expect_peek_identifier_like() {
return None;
}
value.push('.');
value.push_str(&self.cur_token.literal);
}
Some(Identifier::new(token, value))
}
pub(crate) fn is_reserved_keyword(keyword: &str) -> bool {
RESERVED_KEYWORDS.contains(keyword.to_uppercase().as_str())
}
pub(crate) fn cur_token_is_keyword(&self, keyword: &str) -> bool {
self.cur_token.token_type == TokenType::Keyword
&& self.cur_token.literal.eq_ignore_ascii_case(keyword)
}
pub(crate) fn peek_token_is_keyword(&self, keyword: &str) -> bool {
self.peek_token.token_type == TokenType::Keyword
&& self.peek_token.literal.eq_ignore_ascii_case(keyword)
}
pub(crate) fn cur_token_is_punctuator(&self, punc: &str) -> bool {
self.cur_token.token_type == TokenType::Punctuator && self.cur_token.literal == punc
}
pub(crate) fn peek_token_is_punctuator(&self, punc: &str) -> bool {
self.peek_token.token_type == TokenType::Punctuator && self.peek_token.literal == punc
}
pub(crate) fn peek_token_is_operator(&self, op: &str) -> bool {
self.peek_token.token_type == TokenType::Operator && self.peek_token.literal == op
}
pub(crate) fn peek_token_is_identifier_like(&self) -> bool {
match self.peek_token.token_type {
TokenType::Identifier => true,
TokenType::Keyword => !Self::is_reserved_keyword(&self.peek_token.literal),
_ => false,
}
}
pub(crate) fn expect_peek_identifier_like(&mut self) -> bool {
if self.peek_token_is_identifier_like() {
self.next_token();
true
} else {
self.peek_error(TokenType::Identifier);
false
}
}
pub(crate) fn expect_peek(&mut self, t: TokenType) -> bool {
if self.peek_token_is(t) {
self.next_token();
true
} else {
self.peek_error(t);
false
}
}
pub(crate) fn expect_keyword(&mut self, keyword: &str) -> bool {
if self.peek_token_is_keyword(keyword) {
self.next_token();
true
} else {
self.add_error(format!(
"expected {} after {}, got {}",
keyword,
self.cur_token.literal,
Self::format_token_for_error(&self.peek_token)
));
false
}
}
pub(crate) fn peek_precedence(&self) -> Precedence {
match self.peek_token.token_type {
TokenType::Operator => Precedence::for_operator(&self.peek_token.literal),
TokenType::Keyword => Precedence::for_operator(&self.peek_token.literal),
TokenType::Punctuator => {
if self.peek_token.literal == "." {
Precedence::Dot
} else if self.peek_token.literal == "(" {
Precedence::Call
} else if self.peek_token.literal == "[" {
Precedence::Index
} else {
Precedence::Lowest
}
}
_ => Precedence::Lowest,
}
}
pub(crate) fn cur_precedence(&self) -> Precedence {
match self.cur_token.token_type {
TokenType::Operator => Precedence::for_operator(&self.cur_token.literal),
TokenType::Keyword => Precedence::for_operator(&self.cur_token.literal),
TokenType::Punctuator => {
if self.cur_token.literal == "." {
Precedence::Dot
} else if self.cur_token.literal == "(" {
Precedence::Call
} else if self.cur_token.literal == "[" {
Precedence::Index
} else {
Precedence::Lowest
}
}
_ => Precedence::Lowest,
}
}
pub(crate) fn peek_error(&mut self, expected: TokenType) {
let position = self.peek_token.position;
let expected_desc = match expected {
TokenType::Identifier => "identifier (name)",
TokenType::Keyword => "keyword",
TokenType::Punctuator => "'(' or ')'",
TokenType::String => "string literal",
TokenType::Integer => "integer",
TokenType::Float => "number",
_ => "token",
};
if self.peek_token.token_type == TokenType::Eof {
if !self.current_clause.is_empty() {
self.add_error_at(
format!("expected {} after {}", expected_desc, self.current_clause),
position,
);
} else {
self.add_error_at(
format!("unexpected end of input, expected {}", expected_desc),
position,
);
}
} else if expected == TokenType::Identifier
&& self.peek_token.token_type == TokenType::Keyword
&& Self::is_reserved_keyword(&self.peek_token.literal)
{
self.add_error_at(
format!(
"'{}' is a reserved keyword and cannot be used as an identifier. \
Use double quotes to escape it: \"{}\"",
self.peek_token.literal.to_uppercase(),
self.peek_token.literal
),
position,
);
} else {
self.add_error_at(
format!(
"expected {}, got {}",
expected_desc,
Self::format_token_for_error(&self.peek_token)
),
position,
);
}
}
pub(crate) fn format_token_for_error(token: &Token) -> String {
if token.token_type == TokenType::Eof {
"end of input".to_string()
} else {
format!("'{}'", token.literal)
}
}
pub(crate) fn add_error(&mut self, msg: String) {
self.add_error_at(msg, self.cur_token.position);
}
pub(crate) fn add_error_at(&mut self, msg: String, position: super::token::Position) {
self.errors.push(ParseError::new(msg, position));
}
pub(crate) fn source_range_from(&self, start: Position) -> SourceRange {
SourceRange::new(start, self.peek_token.position)
}
pub(crate) fn source_range_through_peek_from(&self, start: Position) -> SourceRange {
let mut end = self.peek_token.position;
end.offset = end.offset.saturating_add(self.peek_token.literal.len());
end.column = end
.column
.saturating_add(self.peek_token.literal.chars().count());
SourceRange::new(start, end)
}
pub(crate) fn normalized_source_for(&self, range: &SourceRange) -> String {
let start = range.start.offset.min(self.source.len());
let end = range.end.offset.min(self.source.len());
self.source[start..end].to_owned()
}
pub(crate) fn enter_expression(&mut self) -> bool {
if self.expression_depth >= MAX_EXPRESSION_NESTING {
self.add_error(format!(
"expression nesting depth exceeds limit of {MAX_EXPRESSION_NESTING}"
));
return false;
}
self.expression_depth += 1;
true
}
pub fn errors(&self) -> &[ParseError] {
&self.errors
}
pub(crate) fn next_parameter_index(&mut self) -> usize {
let idx = self.parameter_counter;
self.parameter_counter += 1;
idx
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parser_creation() {
let parser = Parser::new("SELECT * FROM users");
assert!(parser.cur_token_is_keyword("SELECT"));
}
#[test]
fn test_next_token() {
let mut parser = Parser::new("SELECT * FROM users");
assert!(parser.cur_token_is_keyword("SELECT"));
parser.next_token();
assert!(parser.cur_token_is(TokenType::Operator));
assert_eq!(parser.cur_token.literal, "*");
}
#[test]
fn test_peek_token() {
let parser = Parser::new("SELECT * FROM users");
assert!(parser.cur_token_is_keyword("SELECT"));
assert!(parser.peek_token_is_operator("*"));
}
}