Skip to content

Commit

Permalink
Reorganize the Parser module
Browse files Browse the repository at this point in the history
I mostly did this as an exercise to get a general feel of how the Parser
implementation is organized. The basics here are that for every top
level keyword in Parser::parse_statement I created a new module and
moved the corresponding function to that module. Then I spent a few
hours checking `find references` and any method that was in a single new
module got moved there.

Towards the end I started making some arbitary decisions on where
functions referenced from multiple modules lived. Some of these seemed
obvious, while some were certainly arbitrary.

Most of the motivation here was that working on a 13,000 line file was
causing my editor to be very not happy. After this happy, the largest
module is now src/parser/select.rs which clocks in at 2142 lines.

I should note, that the only visible changes are hopefully a few
functions that had visibility flipped from private to public because I
forgot about pub(crate) when I first started. Other than that, this is
purely copy/paste moving of code to new module files.
  • Loading branch information
davisp committed Dec 7, 2024
1 parent d0fcc06 commit 982a29b
Show file tree
Hide file tree
Showing 71 changed files with 13,519 additions and 13,227 deletions.
652 changes: 494 additions & 158 deletions src/parser/alter.rs

Large diffs are not rendered by default.

59 changes: 59 additions & 0 deletions src/parser/analyze.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
use crate::parser::*;

impl<'a> Parser<'a> {
pub fn parse_analyze(&mut self) -> Result<Statement, ParserError> {
self.expect_keyword(Keyword::TABLE)?;
let table_name = self.parse_object_name(false)?;
let mut for_columns = false;
let mut cache_metadata = false;
let mut noscan = false;
let mut partitions = None;
let mut compute_statistics = false;
let mut columns = vec![];
loop {
match self.parse_one_of_keywords(&[
Keyword::PARTITION,
Keyword::FOR,
Keyword::CACHE,
Keyword::NOSCAN,
Keyword::COMPUTE,
]) {
Some(Keyword::PARTITION) => {
self.expect_token(&Token::LParen)?;
partitions = Some(self.parse_comma_separated(Parser::parse_expr)?);
self.expect_token(&Token::RParen)?;
}
Some(Keyword::NOSCAN) => noscan = true,
Some(Keyword::FOR) => {
self.expect_keyword(Keyword::COLUMNS)?;

columns = self
.maybe_parse(|parser| {
parser.parse_comma_separated(|p| p.parse_identifier(false))
})?
.unwrap_or_default();
for_columns = true
}
Some(Keyword::CACHE) => {
self.expect_keyword(Keyword::METADATA)?;
cache_metadata = true
}
Some(Keyword::COMPUTE) => {
self.expect_keyword(Keyword::STATISTICS)?;
compute_statistics = true
}
_ => break,
}
}

Ok(Statement::Analyze {
table_name,
for_columns,
columns,
partitions,
cache_metadata,
noscan,
compute_statistics,
})
}
}
14 changes: 14 additions & 0 deletions src/parser/assert.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
use crate::parser::*;

impl<'a> Parser<'a> {
pub fn parse_assert(&mut self) -> Result<Statement, ParserError> {
let condition = self.parse_expr()?;
let message = if self.parse_keyword(Keyword::AS) {
Some(self.parse_expr()?)
} else {
None
};

Ok(Statement::Assert { condition, message })
}
}
23 changes: 23 additions & 0 deletions src/parser/assignment.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
use crate::parser::*;

impl<'a> Parser<'a> {
/// Parse a `var = expr` assignment, used in an UPDATE statement
pub fn parse_assignment(&mut self) -> Result<Assignment, ParserError> {
let target = self.parse_assignment_target()?;
self.expect_token(&Token::Eq)?;
let value = self.parse_expr()?;
Ok(Assignment { target, value })
}

/// Parse the left-hand side of an assignment, used in an UPDATE statement
pub fn parse_assignment_target(&mut self) -> Result<AssignmentTarget, ParserError> {
if self.consume_token(&Token::LParen) {
let columns = self.parse_comma_separated(|p| p.parse_object_name(false))?;
self.expect_token(&Token::RParen)?;
Ok(AssignmentTarget::Tuple(columns))
} else {
let column = self.parse_object_name(false)?;
Ok(AssignmentTarget::ColumnName(column))
}
}
}
15 changes: 15 additions & 0 deletions src/parser/attach.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
use crate::parser::*;

impl<'a> Parser<'a> {
pub fn parse_attach_database(&mut self) -> Result<Statement, ParserError> {
let database = self.parse_keyword(Keyword::DATABASE);
let database_file_name = self.parse_expr()?;
self.expect_keyword(Keyword::AS)?;
let schema_name = self.parse_identifier(false)?;
Ok(Statement::AttachDatabase {
database,
schema_name,
database_file_name,
})
}
}
93 changes: 93 additions & 0 deletions src/parser/cache.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
use crate::parser::*;

impl<'a> Parser<'a> {
/// Parse a CACHE TABLE statement
pub fn parse_cache_table(&mut self) -> Result<Statement, ParserError> {
let (mut table_flag, mut options, mut has_as, mut query) = (None, vec![], false, None);
if self.parse_keyword(Keyword::TABLE) {
let table_name = self.parse_object_name(false)?;
if self.peek_token().token != Token::EOF {
if let Token::Word(word) = self.peek_token().token {
if word.keyword == Keyword::OPTIONS {
options = self.parse_options(Keyword::OPTIONS)?
}
};

if self.peek_token().token != Token::EOF {
let (a, q) = self.parse_as_query()?;
has_as = a;
query = Some(q);
}

Ok(Statement::Cache {
table_flag,
table_name,
has_as,
options,
query,
})
} else {
Ok(Statement::Cache {
table_flag,
table_name,
has_as,
options,
query,
})
}
} else {
table_flag = Some(self.parse_object_name(false)?);
if self.parse_keyword(Keyword::TABLE) {
let table_name = self.parse_object_name(false)?;
if self.peek_token() != Token::EOF {
if let Token::Word(word) = self.peek_token().token {
if word.keyword == Keyword::OPTIONS {
options = self.parse_options(Keyword::OPTIONS)?
}
};

if self.peek_token() != Token::EOF {
let (a, q) = self.parse_as_query()?;
has_as = a;
query = Some(q);
}

Ok(Statement::Cache {
table_flag,
table_name,
has_as,
options,
query,
})
} else {
Ok(Statement::Cache {
table_flag,
table_name,
has_as,
options,
query,
})
}
} else {
if self.peek_token() == Token::EOF {
self.prev_token();
}
self.expected("a `TABLE` keyword", self.peek_token())
}
}
}

/// Parse 'AS' before as query,such as `WITH XXX AS SELECT XXX` oer `CACHE TABLE AS SELECT XXX`
pub fn parse_as_query(&mut self) -> Result<(bool, Box<Query>), ParserError> {
match self.peek_token().token {
Token::Word(word) => match word.keyword {
Keyword::AS => {
self.next_token();
Ok((true, self.parse_query()?))
}
_ => Ok((false, self.parse_query()?)),
},
_ => self.expected("a QUERY statement", self.peek_token()),
}
}
}
82 changes: 82 additions & 0 deletions src/parser/call.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
use crate::parser::*;

use crate::parser_err;

impl<'a> Parser<'a> {
/// Parse a `CALL procedure_name(arg1, arg2, ...)`
/// or `CALL procedure_name` statement
pub fn parse_call(&mut self) -> Result<Statement, ParserError> {
let object_name = self.parse_object_name(false)?;
if self.peek_token().token == Token::LParen {
match self.parse_function(object_name)? {
Expr::Function(f) => Ok(Statement::Call(f)),
other => parser_err!(
format!("Expected a simple procedure call but found: {other}"),
self.peek_token().span.start
),
}
} else {
Ok(Statement::Call(Function {
name: object_name,
parameters: FunctionArguments::None,
args: FunctionArguments::None,
over: None,
filter: None,
null_treatment: None,
within_group: vec![],
}))
}
}

pub fn parse_function_desc(&mut self) -> Result<FunctionDesc, ParserError> {
let name = self.parse_object_name(false)?;

let args = if self.consume_token(&Token::LParen) {
if self.consume_token(&Token::RParen) {
None
} else {
let args = self.parse_comma_separated(Parser::parse_function_arg)?;
self.expect_token(&Token::RParen)?;
Some(args)
}
} else {
None
};

Ok(FunctionDesc { name, args })
}

pub(crate) fn parse_function_arg(&mut self) -> Result<OperateFunctionArg, ParserError> {
let mode = if self.parse_keyword(Keyword::IN) {
Some(ArgMode::In)
} else if self.parse_keyword(Keyword::OUT) {
Some(ArgMode::Out)
} else if self.parse_keyword(Keyword::INOUT) {
Some(ArgMode::InOut)
} else {
None
};

// parse: [ argname ] argtype
let mut name = None;
let mut data_type = self.parse_data_type()?;
if let DataType::Custom(n, _) = &data_type {
// the first token is actually a name
name = Some(n.0[0].clone());
data_type = self.parse_data_type()?;
}

let default_expr = if self.parse_keyword(Keyword::DEFAULT) || self.consume_token(&Token::Eq)
{
Some(self.parse_expr()?)
} else {
None
};
Ok(OperateFunctionArg {
mode,
name,
data_type,
default_expr,
})
}
}
15 changes: 15 additions & 0 deletions src/parser/close.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
use crate::parser::*;

impl<'a> Parser<'a> {
pub fn parse_close(&mut self) -> Result<Statement, ParserError> {
let cursor = if self.parse_keyword(Keyword::ALL) {
CloseCursor::All
} else {
let name = self.parse_identifier(false)?;

CloseCursor::Specific { name }
};

Ok(Statement::Close { cursor })
}
}
Loading

0 comments on commit 982a29b

Please sign in to comment.