From 1256f25e6f3c13a45f236a867e280c2ab9fde95c Mon Sep 17 00:00:00 2001 From: Fletcher555 Date: Sat, 6 Sep 2025 12:52:52 -0400 Subject: [PATCH 1/2] Rename CLI module to interpreter --- src/cli/mod.rs | 42 ++++++------------- src/db/database.rs | 4 +- src/db/table/delete/mod.rs | 6 +-- src/db/table/helpers/common.rs | 2 +- src/db/table/helpers/limit_clause.rs | 2 +- src/db/table/helpers/order_by_clause.rs | 4 +- src/db/table/helpers/where_condition.rs | 4 +- src/db/table/helpers/where_stack.rs | 6 +-- src/db/table/insert/mod.rs | 2 +- src/db/table/mod.rs | 2 +- src/db/table/select/mod.rs | 10 ++--- src/db/table/test_utils.rs | 2 +- src/db/table/update/mod.rs | 6 +-- .../ast/create_statement.rs | 4 +- .../ast/delete_statement.rs | 18 ++++---- .../ast/helpers/common.rs | 8 ++-- .../ast/helpers/limit_clause.rs | 8 ++-- src/{cli => interpreter}/ast/helpers/mod.rs | 0 .../ast/helpers/order_by_clause.rs | 8 ++-- .../ast/helpers/where_condition.rs | 10 ++--- .../ast/helpers/where_stack.rs | 8 ++-- .../ast/insert_statement.rs | 4 +- src/{cli => interpreter}/ast/mod.rs | 2 +- src/{cli => interpreter}/ast/parser.rs | 6 +-- .../ast/select_statement.rs | 18 ++++---- src/{cli => interpreter}/ast/test_utils.rs | 4 +- .../ast/update_statement.rs | 22 +++++----- src/interpreter/mod.rs | 34 +++++++++++++++ src/{cli => interpreter}/tokenizer/mod.rs | 2 +- src/{cli => interpreter}/tokenizer/scanner.rs | 2 +- src/{cli => interpreter}/tokenizer/token.rs | 0 src/main.rs | 4 +- 32 files changed, 137 insertions(+), 117 deletions(-) rename src/{cli => interpreter}/ast/create_statement.rs (99%) rename src/{cli => interpreter}/ast/delete_statement.rs (89%) rename src/{cli => interpreter}/ast/helpers/common.rs (94%) rename src/{cli => interpreter}/ast/helpers/limit_clause.rs (93%) rename src/{cli => interpreter}/ast/helpers/mod.rs (100%) rename src/{cli => interpreter}/ast/helpers/order_by_clause.rs (93%) rename src/{cli => interpreter}/ast/helpers/where_condition.rs (96%) rename src/{cli => interpreter}/ast/helpers/where_stack.rs (98%) rename src/{cli => interpreter}/ast/insert_statement.rs (99%) rename src/{cli => interpreter}/ast/mod.rs (99%) rename src/{cli => interpreter}/ast/parser.rs (97%) rename src/{cli => interpreter}/ast/select_statement.rs (94%) rename src/{cli => interpreter}/ast/test_utils.rs (80%) rename src/{cli => interpreter}/ast/update_statement.rs (94%) create mode 100644 src/interpreter/mod.rs rename src/{cli => interpreter}/tokenizer/mod.rs (99%) rename src/{cli => interpreter}/tokenizer/scanner.rs (99%) rename src/{cli => interpreter}/tokenizer/token.rs (100%) diff --git a/src/cli/mod.rs b/src/cli/mod.rs index 56ee1d1..c6be36c 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -1,13 +1,11 @@ use std::io; use crate::db; -pub mod ast; -mod tokenizer; +use crate::interpreter::run_sql; -pub fn cli() { +pub fn cli(database: &mut db::database::Database) { clear_screen(); println!("Welcome to the MollyDB CLI"); let mut line_count = 1; - let mut database = db::database::Database::new(); loop { print!("({:03}) > ", line_count); @@ -29,35 +27,21 @@ pub fn cli() { continue; } - let tokens = tokenizer::tokenize(input); - // println!("{:?}", tokens); - let ast = ast::generate(tokens); - for sql_statement in ast { - // println!("{:?}", sql_statement); - match sql_statement { - Ok(statement) => { - let result = database.execute(statement); - if let Ok(values) = result { - if let Some(rows) = values { - for row in rows { - println!("{:?}", row); - } - } - else { - println!("Executed Successfully"); - } - } - else { - println!("Error: {}", result.unwrap_err()); - } - }, - Err(error) => { - println!("Error: {}", error); - }, + let result = run_sql(database, input); + if let Ok(Some(rows)) = result { + for row in rows { + println!("{:?}", row); } } + else if let Ok(None) = result { + println!("Executed Successfully"); + } + else { + println!("Error: {}", result.unwrap_err()); + } } } + fn clear_screen() { // Clear screen and move cursor to top-left print!("\x1B[2J\x1B[1;1H"); diff --git a/src/db/database.rs b/src/db/database.rs index c1a001b..a28fe26 100644 --- a/src/db/database.rs +++ b/src/db/database.rs @@ -1,5 +1,5 @@ use crate::db::table::{Table, Value}; -use crate::cli::ast::{SqlStatement, CreateTableStatement, InsertIntoStatement, SelectStatement, DeleteStatement, UpdateStatement}; +use crate::interpreter::ast::{SqlStatement, CreateTableStatement, InsertIntoStatement, SelectStatement, DeleteStatement, UpdateStatement}; use crate::db::table::select; use crate::db::table::insert; use crate::db::table::delete; @@ -97,7 +97,7 @@ impl Database { #[cfg(test)] mod tests { use super::*; - use crate::cli::ast::CreateTableStatement; + use crate::interpreter::ast::CreateTableStatement; use crate::db::table::{ColumnDefinition, DataType}; diff --git a/src/db/table/delete/mod.rs b/src/db/table/delete/mod.rs index f2b3e06..b49da25 100644 --- a/src/db/table/delete/mod.rs +++ b/src/db/table/delete/mod.rs @@ -1,7 +1,7 @@ use std::collections::HashSet; use crate::db::table::Table; -use crate::cli::ast::DeleteStatement; +use crate::interpreter::ast::DeleteStatement; use crate::db::table::helpers::common::get_row_indicies_matching_clauses; @@ -44,9 +44,9 @@ fn swap_remove_bulk(table: &mut Table, row_indicies: Vec) -> Result<(), S mod tests { use super::*; use crate::db::table::Value; - use crate::cli::ast::{WhereStackElement, Operator, Operand, WhereCondition, OrderByDirection, OrderByClause}; + use crate::interpreter::ast::{WhereStackElement, Operator, Operand, WhereCondition, OrderByDirection, OrderByClause}; use crate::db::table::test_utils::{default_table, assert_table_rows_eq_unordered}; - use crate::cli::ast::LimitClause; + use crate::interpreter::ast::LimitClause; #[test] fn delete_from_table_works_correctly() { diff --git a/src/db/table/helpers/common.rs b/src/db/table/helpers/common.rs index dba9466..88f78dc 100644 --- a/src/db/table/helpers/common.rs +++ b/src/db/table/helpers/common.rs @@ -1,5 +1,5 @@ use crate::db::table::{Table, Value, DataType}; -use crate::cli::ast::{SelectStatementColumns, WhereStackElement, OrderByClause, LimitClause}; +use crate::interpreter::ast::{SelectStatementColumns, WhereStackElement, OrderByClause, LimitClause}; use crate::db::table::helpers::where_stack::matches_where_stack; use crate::db::table::helpers::{order_by_clause::get_ordered_row_indicies, limit_clause::get_limited_row_indicies}; diff --git a/src/db/table/helpers/limit_clause.rs b/src/db/table/helpers/limit_clause.rs index dd6eced..967d4ce 100644 --- a/src/db/table/helpers/limit_clause.rs +++ b/src/db/table/helpers/limit_clause.rs @@ -1,6 +1,6 @@ use std::cmp::min; -use crate::cli::ast::LimitClause; +use crate::interpreter::ast::LimitClause; use crate::db::table::Value; diff --git a/src/db/table/helpers/order_by_clause.rs b/src/db/table/helpers/order_by_clause.rs index af89f56..a857d93 100644 --- a/src/db/table/helpers/order_by_clause.rs +++ b/src/db/table/helpers/order_by_clause.rs @@ -1,6 +1,6 @@ use std::cmp::Ordering; -use crate::cli::ast::OrderByClause; +use crate::interpreter::ast::OrderByClause; use crate::db::table::Table; use crate::db::table::Value; @@ -36,7 +36,7 @@ fn perform_comparions(table: &Table, row1: &Vec, row2: &Vec, order mod tests { use super::*; use crate::db::table::{Table, Value, DataType, ColumnDefinition}; - use crate::cli::ast::OrderByDirection; + use crate::interpreter::ast::OrderByDirection; fn default_table() -> Table { Table { diff --git a/src/db/table/helpers/where_condition.rs b/src/db/table/helpers/where_condition.rs index dd4ab41..ce8dce2 100644 --- a/src/db/table/helpers/where_condition.rs +++ b/src/db/table/helpers/where_condition.rs @@ -1,5 +1,5 @@ use crate::db::table::{Table, Value}; -use crate::cli::ast::{Operator, Operand, WhereCondition}; +use crate::interpreter::ast::{Operator, Operand, WhereCondition}; use crate::db::table::DataType; @@ -105,7 +105,7 @@ fn expect_same_type(l_side: &Value, r_side: &Value) -> Result<(), String> { mod tests { use super::*; use crate::db::table::{Table, Value, DataType, ColumnDefinition}; - use crate::cli::ast::{Operator, Operand, WhereCondition}; + use crate::interpreter::ast::{Operator, Operand, WhereCondition}; #[test] fn matches_where_clause_returns_true_if_row_matches_where_clause() { diff --git a/src/db/table/helpers/where_stack.rs b/src/db/table/helpers/where_stack.rs index 08804f4..25a3114 100644 --- a/src/db/table/helpers/where_stack.rs +++ b/src/db/table/helpers/where_stack.rs @@ -1,4 +1,4 @@ -use crate::cli::ast::{WhereStackElement, LogicalOperator}; +use crate::interpreter::ast::{WhereStackElement, LogicalOperator}; use crate::db::table::{Table, Value}; use crate::db::table::helpers::where_condition::matches_where_clause; @@ -52,8 +52,8 @@ pub fn matches_where_stack(table: &Table, row: &Vec, where_stack: &Vec WhereStackElement { WhereStackElement::Condition(WhereCondition {l_side: Operand::Identifier(l_side.to_string()), operator, r_side: Operand::Value(r_side)}) diff --git a/src/db/table/insert/mod.rs b/src/db/table/insert/mod.rs index f138c8e..cef9744 100644 --- a/src/db/table/insert/mod.rs +++ b/src/db/table/insert/mod.rs @@ -1,7 +1,7 @@ use std::collections::{HashMap, VecDeque}; use crate::db::table::{Table, Value}; -use crate::cli::ast::InsertIntoStatement; +use crate::interpreter::ast::InsertIntoStatement; use crate::db::table::helpers::common::validate_and_clone_row; diff --git a/src/db/table/mod.rs b/src/db/table/mod.rs index 0b16bd8..7594169 100644 --- a/src/db/table/mod.rs +++ b/src/db/table/mod.rs @@ -1,6 +1,6 @@ use std::cmp::Ordering; -use crate::cli::ast::OrderByDirection; +use crate::interpreter::ast::OrderByDirection; pub mod select; pub mod insert; diff --git a/src/db/table/select/mod.rs b/src/db/table/select/mod.rs index d31fb1a..bce7199 100644 --- a/src/db/table/select/mod.rs +++ b/src/db/table/select/mod.rs @@ -1,5 +1,5 @@ use crate::db::table::{Table, Value}; -use crate::cli::ast::{SelectStatement}; +use crate::interpreter::ast::{SelectStatement}; use crate::db::table::helpers::common::{get_row_indicies_matching_clauses, get_row_columns_from_indicies}; @@ -14,10 +14,10 @@ pub fn select(table: &Table, statement: SelectStatement) -> Result, update mod tests { use super::*; use crate::db::table::{Value, DataType, ColumnDefinition}; - use crate::cli::ast::ColumnValue; + use crate::interpreter::ast::ColumnValue; use crate::db::table::test_utils::{default_table, assert_table_rows_eq_unordered}; - use crate::cli::ast::{WhereStackElement, WhereCondition, Operand, Operator, OrderByClause, OrderByDirection, LimitClause}; + use crate::interpreter::ast::{WhereStackElement, WhereCondition, Operand, Operator, OrderByClause, OrderByDirection, LimitClause}; #[test] fn update_works_correctly() { diff --git a/src/cli/ast/create_statement.rs b/src/interpreter/ast/create_statement.rs similarity index 99% rename from src/cli/ast/create_statement.rs rename to src/interpreter/ast/create_statement.rs index ded96b2..947877d 100644 --- a/src/cli/ast/create_statement.rs +++ b/src/interpreter/ast/create_statement.rs @@ -1,4 +1,4 @@ -use crate::cli::{ +use crate::interpreter::{ ast::{ parser::Parser, CreateTableStatement, SqlStatement::{self, CreateTable}, helpers::common::{expect_token_type, get_table_name} @@ -103,7 +103,7 @@ fn index_statement(_parser: &mut Parser) -> Result { #[cfg(test)] mod tests { use super::*; - use crate::cli::ast::test_utils::token; + use crate::interpreter::ast::test_utils::token; #[test] fn create_table_generates_proper_statement(){ diff --git a/src/cli/ast/delete_statement.rs b/src/interpreter/ast/delete_statement.rs similarity index 89% rename from src/cli/ast/delete_statement.rs rename to src/interpreter/ast/delete_statement.rs index f2fab2e..a04648f 100644 --- a/src/cli/ast/delete_statement.rs +++ b/src/interpreter/ast/delete_statement.rs @@ -1,4 +1,4 @@ -use crate::cli::{ +use crate::interpreter::{ ast::{ parser::Parser, SqlStatement, DeleteStatement, helpers::{ @@ -29,14 +29,14 @@ pub fn build(parser: &mut Parser) -> Result { #[cfg(test)] mod tests { use super::*; - use crate::cli::ast::test_utils::token; - use crate::cli::ast::OrderByClause; - use crate::cli::ast::OrderByDirection; - use crate::cli::ast::LimitClause; - use crate::cli::ast::Operator; - use crate::cli::ast::WhereStackElement; - use crate::cli::ast::WhereCondition; - use crate::cli::ast::Operand; + use crate::interpreter::ast::test_utils::token; + use crate::interpreter::ast::OrderByClause; + use crate::interpreter::ast::OrderByDirection; + use crate::interpreter::ast::LimitClause; + use crate::interpreter::ast::Operator; + use crate::interpreter::ast::WhereStackElement; + use crate::interpreter::ast::WhereCondition; + use crate::interpreter::ast::Operand; use crate::db::table::Value; #[test] diff --git a/src/cli/ast/helpers/common.rs b/src/interpreter/ast/helpers/common.rs similarity index 94% rename from src/cli/ast/helpers/common.rs rename to src/interpreter/ast/helpers/common.rs index 4772771..d16d816 100644 --- a/src/cli/ast/helpers/common.rs +++ b/src/interpreter/ast/helpers/common.rs @@ -1,4 +1,4 @@ -use crate::cli::{ast::{parser::Parser}, tokenizer::token::TokenTypes}; +use crate::interpreter::{ast::{parser::Parser}, tokenizer::token::TokenTypes}; use crate::db::table::Value; @@ -91,9 +91,9 @@ fn decode(hex: &str) -> Result, String> { #[cfg(test)] mod tests { use super::*; - use crate::cli::ast::test_utils::token; - use crate::cli::ast::parser::Parser; - use crate::cli::tokenizer::token::TokenTypes; + use crate::interpreter::ast::test_utils::token; + use crate::interpreter::ast::parser::Parser; + use crate::interpreter::tokenizer::token::TokenTypes; #[test] fn value_list_handles_single_value() { diff --git a/src/cli/ast/helpers/limit_clause.rs b/src/interpreter/ast/helpers/limit_clause.rs similarity index 93% rename from src/cli/ast/helpers/limit_clause.rs rename to src/interpreter/ast/helpers/limit_clause.rs index 8788525..481345a 100644 --- a/src/cli/ast/helpers/limit_clause.rs +++ b/src/interpreter/ast/helpers/limit_clause.rs @@ -1,7 +1,7 @@ -use crate::cli::ast::{parser::Parser, LimitClause}; -use crate::cli::tokenizer::token::TokenTypes; +use crate::interpreter::ast::{parser::Parser, LimitClause}; +use crate::interpreter::tokenizer::token::TokenTypes; use crate::db::table::Value; -use crate::cli::ast::helpers::common::{expect_token_type, token_to_value}; +use crate::interpreter::ast::helpers::common::{expect_token_type, token_to_value}; pub fn get_limit(parser: &mut Parser) -> Result, String> { if expect_token_type(parser, TokenTypes::Limit).is_err() { @@ -40,7 +40,7 @@ pub fn get_limit(parser: &mut Parser) -> Result, String> { #[cfg(test)] mod tests { use super::*; - use crate::cli::ast::test_utils::token; + use crate::interpreter::ast::test_utils::token; #[test] fn limit_clause_is_generated_correctly() { diff --git a/src/cli/ast/helpers/mod.rs b/src/interpreter/ast/helpers/mod.rs similarity index 100% rename from src/cli/ast/helpers/mod.rs rename to src/interpreter/ast/helpers/mod.rs diff --git a/src/cli/ast/helpers/order_by_clause.rs b/src/interpreter/ast/helpers/order_by_clause.rs similarity index 93% rename from src/cli/ast/helpers/order_by_clause.rs rename to src/interpreter/ast/helpers/order_by_clause.rs index 9ccafc4..e31e342 100644 --- a/src/cli/ast/helpers/order_by_clause.rs +++ b/src/interpreter/ast/helpers/order_by_clause.rs @@ -1,7 +1,7 @@ -use crate::cli::ast::{parser::Parser, OrderByClause, OrderByDirection}; -use crate::cli::tokenizer::token::TokenTypes; +use crate::interpreter::ast::{parser::Parser, OrderByClause, OrderByDirection}; +use crate::interpreter::tokenizer::token::TokenTypes; -use crate::cli::ast::helpers::common::expect_token_type; +use crate::interpreter::ast::helpers::common::expect_token_type; pub fn get_order_by(parser: &mut Parser) -> Result>, String> { if expect_token_type(parser, TokenTypes::Order).is_err() { @@ -49,7 +49,7 @@ pub fn get_order_by(parser: &mut Parser) -> Result>, S #[cfg(test)] mod tests { use super::*; - use crate::cli::ast::test_utils::token; + use crate::interpreter::ast::test_utils::token; #[test] fn order_by_clause_is_generated_correctly() { diff --git a/src/cli/ast/helpers/where_condition.rs b/src/interpreter/ast/helpers/where_condition.rs similarity index 96% rename from src/cli/ast/helpers/where_condition.rs rename to src/interpreter/ast/helpers/where_condition.rs index e03e3e8..08df93b 100644 --- a/src/cli/ast/helpers/where_condition.rs +++ b/src/interpreter/ast/helpers/where_condition.rs @@ -1,6 +1,6 @@ -use crate::cli::ast::{parser::Parser, WhereCondition, Operand, Operator}; -use crate::cli::tokenizer::token::TokenTypes; -use crate::cli::ast::helpers::common::{expect_token_type, token_to_value, tokens_to_value_list}; +use crate::interpreter::ast::{parser::Parser, WhereCondition, Operand, Operator}; +use crate::interpreter::tokenizer::token::TokenTypes; +use crate::interpreter::ast::helpers::common::{expect_token_type, token_to_value, tokens_to_value_list}; pub fn get_condition(parser: &mut Parser) -> Result { @@ -76,8 +76,8 @@ pub fn get_operand(parser: &mut Parser) -> Result { #[cfg(test)] mod tests { use super::*; - use crate::cli::ast::{Operator, WhereCondition, Operand}; - use crate::cli::ast::test_utils::token; + use crate::interpreter::ast::{Operator, WhereCondition, Operand}; + use crate::interpreter::ast::test_utils::token; use crate::db::table::Value; fn assert_where_condition(result: Result, expected: WhereCondition, parser: &mut Parser) { diff --git a/src/cli/ast/helpers/where_stack.rs b/src/interpreter/ast/helpers/where_stack.rs similarity index 98% rename from src/cli/ast/helpers/where_stack.rs rename to src/interpreter/ast/helpers/where_stack.rs index cc25817..9c47727 100644 --- a/src/cli/ast/helpers/where_stack.rs +++ b/src/interpreter/ast/helpers/where_stack.rs @@ -1,7 +1,7 @@ -use crate::cli::{ast::{ +use crate::interpreter::{ast::{ helpers::{common::expect_token_type, where_condition::get_condition}, parser::Parser, LogicalOperator, WhereStackElement, WhereStackOperators, Parentheses}}; -use crate::cli::tokenizer::token::TokenTypes; +use crate::interpreter::tokenizer::token::TokenTypes; // The WhereStack is a the method that is used to store the order of operations with Reverse Polish Notation. // This is built from the infix expression of the where clause. Using the shunting yard algorithm. Thanks Djikstra! @@ -166,8 +166,8 @@ fn get_where_condition(parser: &mut Parser) -> Result, #[cfg(test)] mod tests { use super::*; - use crate::cli::ast::{LogicalOperator, Operator, WhereCondition, Operand}; - use crate::cli::ast::test_utils::token; + use crate::interpreter::ast::{LogicalOperator, Operator, WhereCondition, Operand}; + use crate::interpreter::ast::test_utils::token; use crate::db::table::Value; fn simple_condition(l_side: &str, operator: Operator, r_side: Value) -> WhereStackElement { diff --git a/src/cli/ast/insert_statement.rs b/src/interpreter/ast/insert_statement.rs similarity index 99% rename from src/cli/ast/insert_statement.rs rename to src/interpreter/ast/insert_statement.rs index 5dea4d6..aee6135 100644 --- a/src/cli/ast/insert_statement.rs +++ b/src/interpreter/ast/insert_statement.rs @@ -1,4 +1,4 @@ -use crate::cli::{ +use crate::interpreter::{ ast::{ helpers::common::{token_to_value, expect_token_type, get_table_name}, parser::Parser, InsertIntoStatement, SqlStatement::{self, InsertInto} @@ -136,7 +136,7 @@ fn or_statement(_parser: &mut Parser) -> Result { #[cfg(test)] mod tests { use super::*; - use crate::cli::ast::test_utils::token; + use crate::interpreter::ast::test_utils::token; #[test] fn single_row_insert_statement_is_generated_correctly() { diff --git a/src/cli/ast/mod.rs b/src/interpreter/ast/mod.rs similarity index 99% rename from src/cli/ast/mod.rs rename to src/interpreter/ast/mod.rs index 96e880a..1b76159 100644 --- a/src/cli/ast/mod.rs +++ b/src/interpreter/ast/mod.rs @@ -1,4 +1,4 @@ -use crate::cli::tokenizer::{scanner::Token, token::TokenTypes}; +use crate::interpreter::tokenizer::{scanner::Token, token::TokenTypes}; use crate::db::table::{ColumnDefinition, Value}; mod create_statement; diff --git a/src/cli/ast/parser.rs b/src/interpreter/ast/parser.rs similarity index 97% rename from src/cli/ast/parser.rs rename to src/interpreter/ast/parser.rs index f84163c..9494c74 100644 --- a/src/cli/ast/parser.rs +++ b/src/interpreter/ast/parser.rs @@ -1,4 +1,4 @@ -use crate::cli::{ +use crate::interpreter::{ ast::{SqlStatement, StatementBuilder}, tokenizer::scanner::Token, tokenizer::token::TokenTypes, }; @@ -89,8 +89,8 @@ impl<'a> Parser<'a> { #[cfg(test)] mod tests { use super::*; - use crate::cli::ast::{CreateTableStatement, InsertIntoStatement, SelectStatement, SelectStatementColumns}; - use crate::cli::ast::test_utils::{token_with_location, token}; + use crate::interpreter::ast::{CreateTableStatement, InsertIntoStatement, SelectStatement, SelectStatementColumns}; + use crate::interpreter::ast::test_utils::{token_with_location, token}; #[test] fn parser_formats_error_when_at_end_of_input() { diff --git a/src/cli/ast/select_statement.rs b/src/interpreter/ast/select_statement.rs similarity index 94% rename from src/cli/ast/select_statement.rs rename to src/interpreter/ast/select_statement.rs index 0c262c9..7d3d734 100644 --- a/src/cli/ast/select_statement.rs +++ b/src/interpreter/ast/select_statement.rs @@ -1,4 +1,4 @@ -use crate::{cli::{ +use crate::{interpreter::{ ast::{ parser::Parser, SelectStatement, SelectStatementColumns, SqlStatement, WhereStackElement, helpers::{ @@ -44,15 +44,15 @@ fn get_columns(parser: &mut Parser) -> Result { #[cfg(test)] mod tests { use super::*; - use crate::cli::ast::Operator; + use crate::interpreter::ast::Operator; use crate::db::table::Value; - use crate::cli::ast::OrderByClause; - use crate::cli::ast::OrderByDirection; - use crate::cli::ast::LimitClause; - use crate::cli::ast::WhereStackElement; - use crate::cli::ast::WhereCondition; - use crate::cli::ast::test_utils::token; - use crate::cli::ast::Operand; + use crate::interpreter::ast::OrderByClause; + use crate::interpreter::ast::OrderByDirection; + use crate::interpreter::ast::LimitClause; + use crate::interpreter::ast::WhereStackElement; + use crate::interpreter::ast::WhereCondition; + use crate::interpreter::ast::test_utils::token; + use crate::interpreter::ast::Operand; #[test] fn select_statement_with_all_tokens_is_generated_correctly() { diff --git a/src/cli/ast/test_utils.rs b/src/interpreter/ast/test_utils.rs similarity index 80% rename from src/cli/ast/test_utils.rs rename to src/interpreter/ast/test_utils.rs index dbd796f..cadfad5 100644 --- a/src/cli/ast/test_utils.rs +++ b/src/interpreter/ast/test_utils.rs @@ -1,8 +1,8 @@ #[cfg(test)] -use crate::cli::tokenizer::token::TokenTypes; +use crate::interpreter::tokenizer::token::TokenTypes; #[cfg(test)] -use crate::cli::tokenizer::scanner::Token; +use crate::interpreter::tokenizer::scanner::Token; #[cfg(test)] pub fn token(tt: TokenTypes, val: &'static str) -> Token<'static> { diff --git a/src/cli/ast/update_statement.rs b/src/interpreter/ast/update_statement.rs similarity index 94% rename from src/cli/ast/update_statement.rs rename to src/interpreter/ast/update_statement.rs index e16c8fa..4e8b201 100644 --- a/src/cli/ast/update_statement.rs +++ b/src/interpreter/ast/update_statement.rs @@ -1,10 +1,10 @@ -use crate::cli::ast::{ +use crate::interpreter::ast::{ parser::Parser, SqlStatement, UpdateStatement, ColumnValue, helpers::common::{expect_token_type, token_to_value, get_table_name}, helpers::{order_by_clause::get_order_by, limit_clause::get_limit}, }; -use crate::cli::ast::helpers::where_stack::get_where_clause; -use crate::cli::tokenizer::token::TokenTypes; +use crate::interpreter::ast::helpers::where_stack::get_where_clause; +use crate::interpreter::tokenizer::token::TokenTypes; pub fn build(parser: &mut Parser) -> Result { @@ -64,14 +64,14 @@ fn get_update_values(parser: &mut Parser) -> Result, String> { mod tests { use super::*; use crate::db::table::Value; - use crate::cli::ast::Operator; - use crate::cli::ast::WhereStackElement; - use crate::cli::ast::WhereCondition; - use crate::cli::ast::test_utils::token; - use crate::cli::ast::Operand; - use crate::cli::ast::OrderByClause; - use crate::cli::ast::OrderByDirection; - use crate::cli::ast::LimitClause; + use crate::interpreter::ast::Operator; + use crate::interpreter::ast::WhereStackElement; + use crate::interpreter::ast::WhereCondition; + use crate::interpreter::ast::test_utils::token; + use crate::interpreter::ast::Operand; + use crate::interpreter::ast::OrderByClause; + use crate::interpreter::ast::OrderByDirection; + use crate::interpreter::ast::LimitClause; #[test] fn update_statement_with_all_tokens_is_generated_correctly() { diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs new file mode 100644 index 0000000..f2af5a8 --- /dev/null +++ b/src/interpreter/mod.rs @@ -0,0 +1,34 @@ +use crate::db; +use crate::db::table::Value; +pub mod ast; +mod tokenizer; + +pub fn run_sql(database: &mut db::database::Database, sql: &str) -> Result>>, String> { + let tokens = tokenizer::tokenize(sql); + // println!("{:?}", tokens); + let ast = ast::generate(tokens); + + for sql_statement in ast { + // println!("{:?}", sql_statement); + match sql_statement { + Ok(statement) => { + let result = database.execute(statement); + if let Ok(values) = result { + if let Some(rows) = values { + return Ok(Some(rows)); + } + else { + return Ok(None); + } + } + else { + return Err(result.unwrap_err()); + } + }, + Err(error) => { + return Err(error); + }, + } + } + return Ok(None); +} diff --git a/src/cli/tokenizer/mod.rs b/src/interpreter/tokenizer/mod.rs similarity index 99% rename from src/cli/tokenizer/mod.rs rename to src/interpreter/tokenizer/mod.rs index ede66fa..e829dc6 100644 --- a/src/cli/tokenizer/mod.rs +++ b/src/interpreter/tokenizer/mod.rs @@ -1,6 +1,6 @@ pub mod scanner; pub mod token; -use crate::cli::tokenizer::scanner::Token; +use crate::interpreter::tokenizer::scanner::Token; pub fn tokenize<'a>(line: &'a str) -> Vec> { let mut tokens: Vec> = vec![]; diff --git a/src/cli/tokenizer/scanner.rs b/src/interpreter/tokenizer/scanner.rs similarity index 99% rename from src/cli/tokenizer/scanner.rs rename to src/interpreter/tokenizer/scanner.rs index 1e3b22a..b5b48c1 100644 --- a/src/cli/tokenizer/scanner.rs +++ b/src/interpreter/tokenizer/scanner.rs @@ -1,4 +1,4 @@ -use crate::cli::tokenizer::token::{TokenTypes}; +use crate::interpreter::tokenizer::token::{TokenTypes}; #[derive(Debug, PartialEq)] pub struct Token<'a> { diff --git a/src/cli/tokenizer/token.rs b/src/interpreter/tokenizer/token.rs similarity index 100% rename from src/cli/tokenizer/token.rs rename to src/interpreter/tokenizer/token.rs diff --git a/src/main.rs b/src/main.rs index 674b10d..56d993d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,8 @@ +mod interpreter; mod cli; mod db; fn main() { - cli::cli(); + let mut database = db::database::Database::new(); + cli::cli(&mut database); } From cfad02470f71b13dfe13759e7698f4a8126d9724 Mon Sep 17 00:00:00 2001 From: Fletcher555 Date: Sat, 6 Sep 2025 13:48:39 -0400 Subject: [PATCH 2/2] Add integration tests to database --- src/cli/mod.rs | 22 +++++++------- src/interpreter/mod.rs | 13 ++++---- src/lib.rs | 3 ++ src/main.rs | 4 +-- tests/crud_test.rs | 67 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 90 insertions(+), 19 deletions(-) create mode 100644 src/lib.rs create mode 100644 tests/crud_test.rs diff --git a/src/cli/mod.rs b/src/cli/mod.rs index c6be36c..faea065 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -27,17 +27,19 @@ pub fn cli(database: &mut db::database::Database) { continue; } - let result = run_sql(database, input); - if let Ok(Some(rows)) = result { - for row in rows { - println!("{:?}", row); + let results = run_sql(database, input); + for result in results { + if let Ok(Some(rows)) = result { + for row in rows { + println!("{:?}", row); + } + } + else if let Ok(None) = result { + println!("Executed Successfully"); + } + else { + println!("Error: {}", result.unwrap_err()); } - } - else if let Ok(None) = result { - println!("Executed Successfully"); - } - else { - println!("Error: {}", result.unwrap_err()); } } } diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index f2af5a8..012de04 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -3,11 +3,12 @@ use crate::db::table::Value; pub mod ast; mod tokenizer; -pub fn run_sql(database: &mut db::database::Database, sql: &str) -> Result>>, String> { +pub fn run_sql(database: &mut db::database::Database, sql: &str) -> Vec>>, String>> { let tokens = tokenizer::tokenize(sql); // println!("{:?}", tokens); let ast = ast::generate(tokens); + let mut sql_results = vec![]; for sql_statement in ast { // println!("{:?}", sql_statement); match sql_statement { @@ -15,20 +16,20 @@ pub fn run_sql(database: &mut db::database::Database, sql: &str) -> Result { - return Err(error); + sql_results.push(Err(error)); }, } } - return Ok(None); + return sql_results; } diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..f8dd1f3 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,3 @@ +pub mod interpreter; +pub mod db; +pub mod cli; diff --git a/src/main.rs b/src/main.rs index 56d993d..9f0895e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,4 @@ -mod interpreter; -mod cli; -mod db; +use mollydb::*; fn main() { let mut database = db::database::Database::new(); diff --git a/tests/crud_test.rs b/tests/crud_test.rs new file mode 100644 index 0000000..948294e --- /dev/null +++ b/tests/crud_test.rs @@ -0,0 +1,67 @@ +use mollydb::db::database::Database; +use mollydb::interpreter::run_sql; +use mollydb::db::table::Value; + +#[test] +fn test_basic_statements_crud() { + let mut database = Database::new(); + let sql = " + CREATE TABLE users ( + id INTEGER, + name TEXT, + age INTEGER, + money REAL + ); + INSERT INTO users (id, name, age, money) VALUES (1, 'John', 25, 1000.0); + INSERT INTO users (id, name, age, money) VALUES (2, 'Jane', 30, 2000.0); + INSERT INTO users (id, name, age, money) VALUES (3, 'Jim', 35, 3000.0); + UPDATE users SET money = 2000.0 WHERE id = 1; + DELETE FROM users WHERE id = 2; + SELECT * FROM users; + "; + let mut result = run_sql(&mut database, sql); + assert!(result.iter().all(|result| result.is_ok())); + let expected = vec![ + vec![Value::Integer(1), Value::Text("John".to_string()), Value::Integer(25), Value::Real(2000.0)], + vec![Value::Integer(3), Value::Text("Jim".to_string()), Value::Integer(35), Value::Real(3000.0)], + ]; + assert_eq!(result.pop().unwrap().unwrap().unwrap(), expected); + assert!(result.into_iter().all(|result| result.is_ok() && result.unwrap().is_none())); +} + +#[test] +fn test_complex_statements_crud() { + let mut database = Database::new(); + let sql = " + CREATE TABLE users ( + id INTEGER, + name TEXT, + age INTEGER, + money REAL + ); + INSERT INTO users (id, name, age, money) VALUES (1, 'John', 25, 1500.0); + INSERT INTO users (id, name, age, money) VALUES (2, 'Jane', 30, 2000.0); + INSERT INTO users (id, name, age, money) VALUES + (3, 'Jim', 35, 3000.0), + (4, 'John', 70, 1000.0), + (Null, Null, 80, Null); + DELETE FROM users WHERE id >= 2 LIMIT 1 OFFSET 2; + SELECT name, age FROM users ORDER BY age DESC LIMIT 10 OFFSET 1; + UPDATE users SET money = 1000.0 WHERE money IS NULL; + SELECT age FROM users WHERE money = 1000.0; + "; + let mut result = run_sql(&mut database, sql); + assert!(result.iter().all(|result| result.is_ok())); + let expected_first = vec![ + vec![Value::Text("Jim".to_string()), Value::Integer(35)], + vec![Value::Text("Jane".to_string()), Value::Integer(30)], + vec![Value::Text("John".to_string()), Value::Integer(25)], + ]; + let expected_second = vec![ + vec![Value::Integer(80)], + ]; + assert_eq!(expected_second, result.pop().unwrap().unwrap().unwrap()); + assert!(result.pop().unwrap().unwrap().is_none()); + assert_eq!(expected_first, result.pop().unwrap().unwrap().unwrap()); + assert!(result.into_iter().all(|result| result.is_ok() && result.unwrap().is_none())); +} \ No newline at end of file