From 79f782f47a32b34d604113fb340a884939889997 Mon Sep 17 00:00:00 2001 From: Fletcher555 Date: Sun, 31 Aug 2025 18:41:29 -0400 Subject: [PATCH] Fix error messages to display the value instead of token type --- src/cli/ast/mod.rs | 4 ++-- src/cli/ast/parser.rs | 8 ++++---- src/cli/ast/select_statement.rs | 32 +++++++++++++++++++++++++++++++- 3 files changed, 37 insertions(+), 7 deletions(-) diff --git a/src/cli/ast/mod.rs b/src/cli/ast/mod.rs index 340a386..d913e47 100644 --- a/src/cli/ast/mod.rs +++ b/src/cli/ast/mod.rs @@ -173,7 +173,7 @@ mod tests { ]; let result = generate(tokens); assert!(result[0].is_err()); - let expected = vec![Err("Error at line 1, column 0: Unexpected token type: Into".to_string())]; + let expected = vec![Err("Error at line 1, column 0: Unexpected value: INTO".to_string())]; assert_eq!(expected, result); } @@ -242,7 +242,7 @@ mod tests { assert!(result[0].is_err()); assert!(result[1].is_ok()); let expected = vec![ - Err("Error at line 1, column 0: Unexpected token type: SemiColon".to_string()), + Err("Error at line 1, column 0: Unexpected value: ;".to_string()), Ok(SqlStatement::InsertInto(InsertIntoStatement { table_name: "users".to_string(), diff --git a/src/cli/ast/parser.rs b/src/cli/ast/parser.rs index 5d6c630..bfd6caf 100644 --- a/src/cli/ast/parser.rs +++ b/src/cli/ast/parser.rs @@ -49,8 +49,8 @@ impl<'a> Parser<'a> { if self.current < self.tokens.len() { let token = &self.tokens[self.current]; return format!( - "Error at line {:?}, column {:?}: Unexpected token type: {:?}", - token.line_num, token.col_num, token.token_type + "Error at line {:?}, column {:?}: Unexpected value: {}", + token.line_num, token.col_num, token.value.to_string() ); } else { return "Error at end of input.".to_string(); @@ -101,7 +101,7 @@ mod tests { let tokens = vec![token(TokenTypes::Insert, "INSERT", 15, 3)]; let parser = Parser::new(tokens); let result = parser.format_error(); - assert_eq!(result, "Error at line 3, column 15: Unexpected token type: Insert"); + assert_eq!(result, "Error at line 3, column 15: Unexpected value: INSERT"); } pub struct MockStatementBuilder; @@ -196,7 +196,7 @@ mod tests { let mut parser = Parser::new(tokens); let builder : &dyn StatementBuilder = &MockStatementBuilder; let result = parser.next_statement(builder); - let expected = Some(Err("Error at line 1, column 1: Unexpected token type: Identifier".to_string())); + let expected = Some(Err("Error at line 1, column 1: Unexpected value: users".to_string())); assert_eq!(result, expected); } } diff --git a/src/cli/ast/select_statement.rs b/src/cli/ast/select_statement.rs index 548e065..9facd12 100644 --- a/src/cli/ast/select_statement.rs +++ b/src/cli/ast/select_statement.rs @@ -1,4 +1,4 @@ -use crate::cli::{ast::{parser::Parser, SqlStatement, SelectStatement, SelectStatementColumns, Operator, common::token_to_value, common::tokens_to_identifier_list, common::expect_token_type, WhereClause, OrderByClause, OrderByDirection, LimitClause}, tokenizer::token::TokenTypes}; +use crate::{cli::{ast::{common::{expect_token_type, token_to_value, tokens_to_identifier_list}, parser::Parser, LimitClause, Operator, OrderByClause, OrderByDirection, SelectStatement, SelectStatementColumns, SqlStatement, WhereClause}, tokenizer::token::TokenTypes}, db::table::Value}; pub fn build(parser: &mut Parser) -> Result { parser.advance()?; @@ -138,6 +138,11 @@ fn get_limit(parser: &mut Parser) -> Result, String> { expect_token_type(parser, TokenTypes::IntLiteral)?; let offset = token_to_value(parser)?; + if let Value::Integer(offset) = offset { + if offset < 0 { + return Err(parser.format_error()); + } + }; parser.advance()?; return Ok(Some(LimitClause { @@ -165,6 +170,7 @@ mod tests { #[test] fn select_statement_with_all_tokens_is_generated_correctly() { + // SELECT * FROM users; let tokens = vec![ token(TokenTypes::Select, "SELECT"), token(TokenTypes::Asterisk, "*"), @@ -187,6 +193,7 @@ mod tests { #[test] fn select_statement_with_a_single_column_is_generated_correctly() { + // SELECT id FROM guests; let tokens = vec![ token(TokenTypes::Select, "SELECT"), token(TokenTypes::Identifier, "id"), @@ -211,6 +218,7 @@ mod tests { #[test] fn select_statement_with_multiple_columns_is_generated_correctly() { + // SELECT id, name FROM users; let tokens = vec![ token(TokenTypes::Select, "SELECT"), token(TokenTypes::Identifier, "id"), @@ -238,6 +246,7 @@ mod tests { #[test] fn select_statement_with_all_clauses_is_generated_correctly() { + // SELECT id FROM guests WHERE id = 1 ORDER BY id ASC, name DESC, age ASC LIMIT 10 OFFSET 5; let tokens = vec![ token(TokenTypes::Select, "SELECT"), token(TokenTypes::Identifier, "id"), @@ -299,6 +308,7 @@ mod tests { #[test] fn select_statement_with_limit_clause_no_offset_is_generated_correctly() { + // SELECT id FROM guests WHERE id > 1 LIMIT 10; let tokens = vec![ token(TokenTypes::Select, "SELECT"), token(TokenTypes::Identifier, "id"), @@ -333,4 +343,24 @@ mod tests { }), })); } + + #[test] + fn select_statement_with_limit_clause_with_negative_offset_is_generated_correctly() { + // SELECT id FROM guests LIMIT 10 OFFSET -5; + let tokens = vec![ + token(TokenTypes::Select, "SELECT"), + token(TokenTypes::Identifier, "id"), + token(TokenTypes::From, "FROM"), + token(TokenTypes::Identifier, "guests"), + token(TokenTypes::Limit, "LIMIT"), + token(TokenTypes::IntLiteral, "10"), + token(TokenTypes::Offset, "OFFSET"), + token(TokenTypes::IntLiteral, "-5"), + token(TokenTypes::SemiColon, ";"), + ]; + let mut parser = Parser::new(tokens); + let result = build(&mut parser); + assert!(result.is_err()); + assert_eq!(result.unwrap_err(), "Error at line 1, column 0: Unexpected value: -5"); + } } \ No newline at end of file