mod condition;
mod dml;
mod select;
#[cfg(test)]
mod tests;
use wasm_dbms_api::prelude::SqlError;
use crate::ast::Statement;
use crate::lexer::{Keyword, Position, Spanned, Token, tokenize};
pub(crate) const MAX_CONDITION_TERMS: usize = 256;
const STATEMENT_START: &str =
"a statement (SELECT, INSERT, UPDATE, DELETE, BEGIN, COMMIT or ROLLBACK)";
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ParsedStatement {
pub statement: Statement,
pub parameter_count: usize,
}
pub fn parse(sql: &str) -> Result<ParsedStatement, SqlError> {
let mut parser = Parser::new(sql)?;
let statement = parser.statement()?;
parser.finish()?;
Ok(ParsedStatement {
statement,
parameter_count: parser.parameters,
})
}
struct Parser {
tokens: Vec<Spanned>,
cursor: usize,
parameters: usize,
condition_terms: usize,
}
impl Parser {
fn new(sql: &str) -> Result<Self, SqlError> {
Ok(Self {
tokens: tokenize(sql)?,
cursor: 0,
parameters: 0,
condition_terms: 0,
})
}
fn peek_at(&self, offset: usize) -> &Token {
let index = (self.cursor + offset).min(self.tokens.len() - 1);
&self.tokens[index].token
}
fn peek(&self) -> &Token {
self.peek_at(0)
}
fn position(&self) -> Position {
self.tokens[self.cursor].position
}
fn advance(&mut self) {
if self.cursor < self.tokens.len() - 1 {
self.cursor += 1;
}
}
fn at_keyword(&self, keyword: Keyword) -> bool {
*self.peek() == Token::Keyword(keyword)
}
fn eat(&mut self, token: &Token) -> bool {
let found = self.peek() == token;
if found {
self.advance();
}
found
}
fn eat_keyword(&mut self, keyword: Keyword) -> bool {
self.eat(&Token::Keyword(keyword))
}
fn error(&self, msg: impl Into<String>) -> SqlError {
self.position().error(msg)
}
fn unexpected(&self, expected: &str) -> SqlError {
self.error(format!(
"expected {expected}, found {found}",
found = self.peek().describe()
))
}
fn expect(&mut self, token: &Token, expected: &str) -> Result<(), SqlError> {
if self.eat(token) {
Ok(())
} else {
Err(self.unexpected(expected))
}
}
fn expect_keyword(&mut self, keyword: Keyword) -> Result<(), SqlError> {
let expected = format!("`{keyword}`", keyword = keyword.as_str());
self.expect(&Token::Keyword(keyword), &expected)
}
fn identifier(&mut self, expected: &str) -> Result<String, SqlError> {
match self.peek() {
Token::Identifier(name) => {
let name = name.clone();
self.advance();
Ok(name)
}
Token::Keyword(_) => Err(self.error(format!(
"expected {expected}, found {found} (reserved words must be double-quoted to be \
used as names)",
found = self.peek().describe()
))),
_ => Err(self.unexpected(expected)),
}
}
fn next_parameter(&mut self) -> usize {
let index = self.parameters;
self.parameters += 1;
index
}
fn statement(&mut self) -> Result<Statement, SqlError> {
match self.peek() {
Token::Keyword(Keyword::Select) => self
.select()
.map(|select| Statement::Select(Box::new(select))),
Token::Keyword(Keyword::Insert) => self.insert().map(Statement::Insert),
Token::Keyword(Keyword::Update) => self.update().map(Statement::Update),
Token::Keyword(Keyword::Delete) => self.delete().map(Statement::Delete),
Token::Keyword(Keyword::Begin) => Ok(self.transaction(Statement::Begin)),
Token::Keyword(Keyword::Commit) => Ok(self.transaction(Statement::Commit)),
Token::Keyword(Keyword::Rollback) => Ok(self.transaction(Statement::Rollback)),
_ => Err(self.unexpected(STATEMENT_START)),
}
}
fn transaction(&mut self, statement: Statement) -> Statement {
self.advance();
self.eat_keyword(Keyword::Transaction);
statement
}
fn finish(&mut self) -> Result<(), SqlError> {
let terminated = self.eat(&Token::Semicolon);
match self.peek() {
Token::Eof => Ok(()),
_ if terminated => Err(self.error("multiple statements are not supported")),
_ => Err(self.unexpected("end of statement")),
}
}
}