Put parsing details into one module

This commit is contained in:
Yuriy Dupyn 2024-01-26 19:45:15 +01:00
parent 61c0a34253
commit 6000b1f242
10 changed files with 30 additions and 29 deletions

View file

@ -1,122 +0,0 @@
use crate::common::{parse_table_name, parse_column_name, parse_condition};
use minisql::operation::{ColumnSelection, Operation};
use nom::{
branch::alt,
bytes::complete::tag,
character::complete::{multispace0, multispace1, char},
combinator::map,
error::Error,
multi::separated_list0,
sequence::terminated,
IResult,
};
pub fn parse_select(input: &str) -> IResult<&str, Operation> {
let (input, _) = tag("SELECT")(input)?;
let (input, _) = multispace1(input)?;
let (input, column_selection) = try_parse_column_selection(input)?;
let (input, _) = multispace0(input)?;
let (input, _) = tag("FROM")(input)?;
let (input, _) = multispace1(input)?;
let (input, table_name) = parse_table_name(input)?;
let (input, _) = multispace0(input)?;
let (input, condition) = parse_condition(input)?;
let (input, _) = multispace0(input)?;
// TODO: make it optional?
let (input, _) = tag(";")(input)?;
Ok((
input,
Operation::Select(table_name.to_string(), column_selection, condition),
))
}
pub fn try_parse_column_selection(input: &str) -> IResult<&str, ColumnSelection> {
let all_parser = map(tag::<&str, &str, Error<&str>>("*"), |_| {
ColumnSelection::All
});
let columns_parser = map(
separated_list0(terminated(char(','), multispace0), parse_column_name),
|names| ColumnSelection::Columns(names),
);
alt((all_parser, columns_parser))(input)
}
#[cfg(test)]
mod tests {
use minisql::operation::{ColumnSelection, Operation};
use crate::{common::{parse_column_name, parse_table_name}, select::parse_select};
#[test]
fn test_parse_select_all() {
let sql = "SELECT * FROM \"MyTable\";";
let operation = parse_select(sql).expect("should parse");
match operation {
("", Operation::Select(table_name, column_selection, maybe_condition)) => {
assert_eq!(table_name, "MyTable");
assert!(matches!(column_selection, ColumnSelection::All));
assert!(matches!(maybe_condition, None));
}
(input, _) => {
println!("Input to be parsed: {}", input);
panic!("expected select operation")
}
}
}
#[test]
fn test_parse_column_name() {
parse_column_name("1abc").expect_err("variable names should not start with number");
}
#[test]
fn test_parse_table_name() {
parse_table_name("\"\"").expect_err("Empty table names are not allowed");
}
#[test]
fn test_parse_select_columns() {
let sql = "SELECT name , email FROM \"AddressBook\" ;";
let operation = parse_select(sql).expect("should parse");
match operation {
("", Operation::Select(table_name, column_selection, maybe_condition)) => {
assert_eq!(table_name, "AddressBook");
assert!(matches!(column_selection, ColumnSelection::Columns(_)));
match column_selection {
ColumnSelection::Columns(column_names) => {
assert_eq!(column_names, vec!["name", "email"]);
}
_ => {
panic!("should select columns")
}
}
assert!(matches!(maybe_condition, None));
}
(input, _) => {
println!("Input to be parsed: {}", input);
panic!("expected select operation")
}
}
}
#[test]
fn test_parse_select_where() {
use minisql::operation::Condition;
let sql = "SELECT * FROM \"AddressBook\" WHERE id = 5 ;";
let operation = parse_select(sql).expect("should parse");
match operation {
("", Operation::Select(table_name, column_selection, maybe_condition)) => {
assert_eq!(table_name, "AddressBook");
assert!(matches!(column_selection, ColumnSelection::All));
assert!(matches!(maybe_condition, Some(Condition::Eq(_, _))));
}
(input, _) => {
println!("Input to be parsed: {}", input);
panic!("expected select operation")
}
}
}
// TODO: a test with multiple statements
// TODO: allow underscores in identifiers
}