From 442c4b7bebb2d127784816073775006630d63562 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kha=C3=AFs=20COLIN?= Date: Sat, 3 May 2025 21:25:19 +0200 Subject: [PATCH 01/10] feat(error): display filename with the error message --- notes.org | 8 ++++++-- src/error_display.rs | 14 +++++++------- src/main.rs | 2 +- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/notes.org b/notes.org index 39d3be7..05e6c16 100644 --- a/notes.org +++ b/notes.org @@ -11,8 +11,12 @@ :LOGBOOK: CLOCK: [2025-05-03 sam. 18:40]--[2025-05-03 sam. 18:46] => 0:06 :END: -** TODO OSDBError::display() should take a filename and display it alongside the error - +** DONE OSDBError::display() should take a filename and display it alongside the error +:LOGBOOK: +CLOCK: [2025-05-03 sam. 21:24]--[2025-05-03 sam. 21:27] => 0:03 +:END: +** TODO OSDBError::display() should take an input string, to be able to resolve spans inside the error +** TODO OSDBError::display() should generate ariadne errors and return those * DONE snapshot testing ** DONE Find the snapshot testing library diff --git a/src/error_display.rs b/src/error_display.rs index 5a302ff..57a1bdf 100644 --- a/src/error_display.rs +++ b/src/error_display.rs @@ -4,23 +4,23 @@ use crate::{ }; pub trait OSDBError { - fn display(&self); + fn display(&self, file: &str); } impl OSDBError for MetaCommandParseError { - fn display(&self) { - println!("{self}") + fn display(&self, file: &str) { + println!("{file}: {self}") } } impl OSDBError for StatementParseError { - fn display(&self) { - println!("{self}") + fn display(&self, file: &str) { + println!("{file}: {self}") } } impl OSDBError for CommandParseError { - fn display(&self) { - println!("{self}") + fn display(&self, file: &str) { + println!("{file}: {self}") } } diff --git a/src/main.rs b/src/main.rs index a0fe1d0..99063ea 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,7 +14,7 @@ fn main() { break; } } - Err(err) => err.display(), + Err(err) => err.display(""), } } println!("Good-bye"); From 4246775db558a5ea56704607897aea5f6bb257e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kha=C3=AFs=20COLIN?= Date: Sat, 3 May 2025 21:29:40 +0200 Subject: [PATCH 02/10] refactor(errors): error display function takes an input string to be able to resolve spans in the future --- notes.org | 10 ++++++++-- src/error_display.rs | 8 ++++---- src/main.rs | 2 +- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/notes.org b/notes.org index 05e6c16..00cd0a9 100644 --- a/notes.org +++ b/notes.org @@ -13,9 +13,15 @@ CLOCK: [2025-05-03 sam. 18:40]--[2025-05-03 sam. 18:46] => 0:06 :END: ** DONE OSDBError::display() should take a filename and display it alongside the error :LOGBOOK: -CLOCK: [2025-05-03 sam. 21:24]--[2025-05-03 sam. 21:27] => 0:03 +CLOCK: [2025-05-03 sam. 21:24]--[2025-05-03 sam. 21:28] => 0:04 +:END: +** DONE OSDBError::display() should take an input string, to be able to resolve spans inside the error +:PROPERTIES: +:EFFORT: 10 +:END: +:LOGBOOK: +CLOCK: [2025-05-03 sam. 21:28]--[2025-05-03 sam. 21:30] => 0:02 :END: -** TODO OSDBError::display() should take an input string, to be able to resolve spans inside the error ** TODO OSDBError::display() should generate ariadne errors and return those * DONE snapshot testing diff --git a/src/error_display.rs b/src/error_display.rs index 57a1bdf..3cc2a3d 100644 --- a/src/error_display.rs +++ b/src/error_display.rs @@ -4,23 +4,23 @@ use crate::{ }; pub trait OSDBError { - fn display(&self, file: &str); + fn display(&self, file: &str, input: &str); } impl OSDBError for MetaCommandParseError { - fn display(&self, file: &str) { + fn display(&self, file: &str, _input: &str) { println!("{file}: {self}") } } impl OSDBError for StatementParseError { - fn display(&self, file: &str) { + fn display(&self, file: &str, _input: &str) { println!("{file}: {self}") } } impl OSDBError for CommandParseError { - fn display(&self, file: &str) { + fn display(&self, file: &str, _input: &str) { println!("{file}: {self}") } } diff --git a/src/main.rs b/src/main.rs index 99063ea..5757d01 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,7 +14,7 @@ fn main() { break; } } - Err(err) => err.display(""), + Err(err) => err.display("", &input), } } println!("Good-bye"); From 51569d3ec2143d715d925544b093ad091034ed8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kha=C3=AFs=20COLIN?= Date: Sat, 3 May 2025 21:31:26 +0200 Subject: [PATCH 03/10] feat(errors): display basic errors with ariadne --- Cargo.lock | 23 +++++++++++++++++++++++ Cargo.toml | 1 + notes.org | 11 ++++++++++- src/error_display.rs | 24 ++++++++++++++++++------ 4 files changed, 52 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1b637c2..35cd7e3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,16 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "ariadne" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36f5e3dca4e09a6f340a61a0e9c7b61e030c69fc27bf29d73218f7e5e3b7638f" +dependencies = [ + "unicode-width", + "yansi", +] + [[package]] name = "console" version = "0.15.11" @@ -47,6 +57,7 @@ checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" name = "osdb" version = "0.1.0" dependencies = [ + "ariadne", "insta", ] @@ -56,6 +67,12 @@ version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa" +[[package]] +name = "unicode-width" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + [[package]] name = "windows-sys" version = "0.59.0" @@ -128,3 +145,9 @@ name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "yansi" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" diff --git a/Cargo.toml b/Cargo.toml index c53c9f7..6ba1a65 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,7 @@ edition = "2024" authors = ["Khaïs COLIN"] [dependencies] +ariadne = "0.5.1" [dev-dependencies] insta = "1.43.1" diff --git a/notes.org b/notes.org index 00cd0a9..e1d1f54 100644 --- a/notes.org +++ b/notes.org @@ -22,7 +22,14 @@ CLOCK: [2025-05-03 sam. 21:24]--[2025-05-03 sam. 21:28] => 0:04 :LOGBOOK: CLOCK: [2025-05-03 sam. 21:28]--[2025-05-03 sam. 21:30] => 0:02 :END: -** TODO OSDBError::display() should generate ariadne errors and return those +** DONE OSDBError::display() should generate ariadne errors and return those +:PROPERTIES: +:EFFORT: 10 +:END: +:LOGBOOK: +CLOCK: [2025-05-03 sam. 21:30]--[2025-05-03 sam. 21:50] => 0:20 +:END: +** TODO error display should include a span to show where the error occured * DONE snapshot testing ** DONE Find the snapshot testing library @@ -99,3 +106,5 @@ CLOCK: [2025-05-03 sam. 21:21]--[2025-05-03 sam. 21:22] => 0:01 :LOGBOOK: CLOCK: [2025-05-03 sam. 19:06]--[2025-05-03 sam. 19:07] => 0:01 :END: + +* TODO switch statement parsing to more extensible token-based algorithm diff --git a/src/error_display.rs b/src/error_display.rs index 3cc2a3d..e3819bb 100644 --- a/src/error_display.rs +++ b/src/error_display.rs @@ -2,25 +2,37 @@ use crate::{ command::CommandParseError, meta_commands::MetaCommandParseError, statements::StatementParseError, }; +use ariadne::{Report, ReportKind, Source}; pub trait OSDBError { fn display(&self, file: &str, input: &str); } impl OSDBError for MetaCommandParseError { - fn display(&self, file: &str, _input: &str) { - println!("{file}: {self}") + fn display(&self, file: &str, input: &str) { + Report::build(ReportKind::Error, (file, 0..input.len())) + .with_message(format!("{self}")) + .finish() + .print((file, Source::from(input))) + .unwrap(); } } impl OSDBError for StatementParseError { - fn display(&self, file: &str, _input: &str) { - println!("{file}: {self}") + fn display(&self, file: &str, input: &str) { + Report::build(ReportKind::Error, (file, 0..input.len())) + .with_message(format!("{self}")) + .finish() + .print((file, Source::from(input))) + .unwrap(); } } impl OSDBError for CommandParseError { - fn display(&self, file: &str, _input: &str) { - println!("{file}: {self}") + fn display(&self, file: &str, input: &str) { + match self { + CommandParseError::MetaCommand(err) => err.display(file, input), + CommandParseError::Statement(err) => err.display(file, input), + } } } From fe6573b2deb43c3a00dae317ceaf84bc3797ce8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kha=C3=AFs=20COLIN?= Date: Sat, 3 May 2025 21:55:06 +0200 Subject: [PATCH 04/10] feat(error): display location of error --- notes.org | 9 ++++++++- src/error_display.rs | 16 +++++++++++++--- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/notes.org b/notes.org index e1d1f54..852f60e 100644 --- a/notes.org +++ b/notes.org @@ -29,7 +29,14 @@ CLOCK: [2025-05-03 sam. 21:28]--[2025-05-03 sam. 21:30] => 0:02 :LOGBOOK: CLOCK: [2025-05-03 sam. 21:30]--[2025-05-03 sam. 21:50] => 0:20 :END: -** TODO error display should include a span to show where the error occured +** DONE error display should include a span to show where the error occured +:PROPERTIES: +:EFFORT: 10 +:END: +:LOGBOOK: +CLOCK: [2025-05-03 sam. 21:51]--[2025-05-03 sam. 21:54] => 0:03 +:END: +** TODO deduplicate error display code * DONE snapshot testing ** DONE Find the snapshot testing library diff --git a/src/error_display.rs b/src/error_display.rs index e3819bb..ffaae9e 100644 --- a/src/error_display.rs +++ b/src/error_display.rs @@ -2,7 +2,7 @@ use crate::{ command::CommandParseError, meta_commands::MetaCommandParseError, statements::StatementParseError, }; -use ariadne::{Report, ReportKind, Source}; +use ariadne::{Color, Label, Report, ReportKind, Source}; pub trait OSDBError { fn display(&self, file: &str, input: &str); @@ -10,8 +10,13 @@ pub trait OSDBError { impl OSDBError for MetaCommandParseError { fn display(&self, file: &str, input: &str) { - Report::build(ReportKind::Error, (file, 0..input.len())) + Report::build(ReportKind::Error, (file, 0..input.len() - 1)) .with_message(format!("{self}")) + .with_label( + Label::new((file, 0..input.len() - 1)) + .with_color(Color::Red) + .with_message("here"), + ) .finish() .print((file, Source::from(input))) .unwrap(); @@ -20,8 +25,13 @@ impl OSDBError for MetaCommandParseError { impl OSDBError for StatementParseError { fn display(&self, file: &str, input: &str) { - Report::build(ReportKind::Error, (file, 0..input.len())) + Report::build(ReportKind::Error, (file, 0..input.len() - 1)) .with_message(format!("{self}")) + .with_label( + Label::new((file, 0..input.len() - 1)) + .with_color(Color::Red) + .with_message("here"), + ) .finish() .print((file, Source::from(input))) .unwrap(); From 825511a51541dbad47dc88d116f5e3d027c252b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kha=C3=AFs=20COLIN?= Date: Sat, 3 May 2025 21:56:12 +0200 Subject: [PATCH 05/10] refactor(error): deduplicate error display code --- notes.org | 10 ++++++++-- src/error_display.rs | 45 +++++++------------------------------------- 2 files changed, 15 insertions(+), 40 deletions(-) diff --git a/notes.org b/notes.org index 852f60e..c966b68 100644 --- a/notes.org +++ b/notes.org @@ -1,6 +1,6 @@ #+title: Notes -* TODO show errors with ariadne +* DONE show errors with ariadne :PROPERTIES: :EFFORT: 10min :END: @@ -36,7 +36,13 @@ CLOCK: [2025-05-03 sam. 21:30]--[2025-05-03 sam. 21:50] => 0:20 :LOGBOOK: CLOCK: [2025-05-03 sam. 21:51]--[2025-05-03 sam. 21:54] => 0:03 :END: -** TODO deduplicate error display code +** DONE deduplicate error display code +:PROPERTIES: +:EFFORT: 10 +:END: +:LOGBOOK: +CLOCK: [2025-05-03 sam. 21:55]--[2025-05-03 sam. 22:01] => 0:06 +:END: * DONE snapshot testing ** DONE Find the snapshot testing library diff --git a/src/error_display.rs b/src/error_display.rs index ffaae9e..048c1dd 100644 --- a/src/error_display.rs +++ b/src/error_display.rs @@ -1,48 +1,17 @@ -use crate::{ - command::CommandParseError, meta_commands::MetaCommandParseError, - statements::StatementParseError, -}; +use crate::command::CommandParseError; use ariadne::{Color, Label, Report, ReportKind, Source}; pub trait OSDBError { fn display(&self, file: &str, input: &str); } -impl OSDBError for MetaCommandParseError { - fn display(&self, file: &str, input: &str) { - Report::build(ReportKind::Error, (file, 0..input.len() - 1)) - .with_message(format!("{self}")) - .with_label( - Label::new((file, 0..input.len() - 1)) - .with_color(Color::Red) - .with_message("here"), - ) - .finish() - .print((file, Source::from(input))) - .unwrap(); - } -} - -impl OSDBError for StatementParseError { - fn display(&self, file: &str, input: &str) { - Report::build(ReportKind::Error, (file, 0..input.len() - 1)) - .with_message(format!("{self}")) - .with_label( - Label::new((file, 0..input.len() - 1)) - .with_color(Color::Red) - .with_message("here"), - ) - .finish() - .print((file, Source::from(input))) - .unwrap(); - } -} - impl OSDBError for CommandParseError { fn display(&self, file: &str, input: &str) { - match self { - CommandParseError::MetaCommand(err) => err.display(file, input), - CommandParseError::Statement(err) => err.display(file, input), - } + Report::build(ReportKind::Error, (file, 0..input.len() - 1)) + .with_message(format!("{self}")) + .with_label(Label::new((file, 0..input.len() - 1)).with_color(Color::Red)) + .finish() + .print((file, Source::from(input))) + .unwrap(); } } From cbc4a4755c492010576289dd63356dda3f4da20c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kha=C3=AFs=20COLIN?= Date: Sun, 4 May 2025 12:06:47 +0200 Subject: [PATCH 06/10] feat(tokenizer): recognize meta-commands --- notes.org | 53 +++++ src/meta_commands.rs | 2 +- ..._tokens__tests__tokenize_meta_command.snap | 28 +++ ..._tests__tokenize_unknown_meta_command.snap | 18 ++ src/tokens.rs | 182 ++++++++++++------ 5 files changed, 221 insertions(+), 62 deletions(-) create mode 100644 src/snapshots/osdb__tokens__tests__tokenize_meta_command.snap create mode 100644 src/snapshots/osdb__tokens__tests__tokenize_unknown_meta_command.snap diff --git a/notes.org b/notes.org index c966b68..c440953 100644 --- a/notes.org +++ b/notes.org @@ -121,3 +121,56 @@ CLOCK: [2025-05-03 sam. 19:06]--[2025-05-03 sam. 19:07] => 0:01 :END: * TODO switch statement parsing to more extensible token-based algorithm +:PROPERTIES: +:EFFORT: 10 +:END: +:LOGBOOK: +CLOCK: [2025-05-04 dim. 12:07]--[2025-05-04 dim. 12:10] => 0:03 +:END: + +** TODO use tokens to parse meta-commands +:PROPERTIES: +:EFFORT: 10 +:END: +:LOGBOOK: +CLOCK: [2025-05-04 dim. 12:10]--[2025-05-04 dim. 12:22] => 0:12 +:END: + +*** DONE recognize meta-commands as tokens +:PROPERTIES: +:EFFORT: 10 +:END: +:LOGBOOK: +CLOCK: [2025-05-04 dim. 13:32]--[2025-05-04 dim. 13:35] => 0:03 +CLOCK: [2025-05-04 dim. 13:27]--[2025-05-04 dim. 13:32] => 0:05 +:END: + +*** TODO CommandParseError must have a ScanError variant with an Into impl +:PROPERTIES: +:EFFORT: 10 +:END: + +*** TODO ScanErrors must be convertible to ariadne reports +:PROPERTIES: +:EFFORT: 10 +:END: + +**** TODO Remove the CommandParseError Display implementation +:PROPERTIES: +:EFFORT: 10 +:END: + +*** TODO remove token types which are not recognized at all +:PROPERTIES: +:EFFORT: 10 +:END: + +*** TODO parse tokens into meta-commands +:PROPERTIES: +:EFFORT: 10 +:END: + +** TODO use tokens to parse statements +:PROPERTIES: +:EFFORT: +:END: diff --git a/src/meta_commands.rs b/src/meta_commands.rs index e81beb9..e0d42c4 100644 --- a/src/meta_commands.rs +++ b/src/meta_commands.rs @@ -1,4 +1,4 @@ -#[derive(Debug)] +#[derive(Debug, Eq, PartialEq)] pub enum MetaCommand { Exit, } diff --git a/src/snapshots/osdb__tokens__tests__tokenize_meta_command.snap b/src/snapshots/osdb__tokens__tests__tokenize_meta_command.snap new file mode 100644 index 0000000..24454ab --- /dev/null +++ b/src/snapshots/osdb__tokens__tests__tokenize_meta_command.snap @@ -0,0 +1,28 @@ +--- +source: src/tokens.rs +expression: "tokenize(\".exit\".to_string(), \"\".to_string())" +--- +Ok( + [ + Token { + location: Location { + file: "", + offset: 0, + length: 5, + }, + data: MetaCommand( + Exit, + ), + lexeme: ".exit", + }, + Token { + location: Location { + file: "", + offset: 5, + length: 0, + }, + data: EndOfFile, + lexeme: "", + }, + ], +) diff --git a/src/snapshots/osdb__tokens__tests__tokenize_unknown_meta_command.snap b/src/snapshots/osdb__tokens__tests__tokenize_unknown_meta_command.snap new file mode 100644 index 0000000..9e04320 --- /dev/null +++ b/src/snapshots/osdb__tokens__tests__tokenize_unknown_meta_command.snap @@ -0,0 +1,18 @@ +--- +source: src/tokens.rs +expression: "tokenize(\".halp\".to_string(), \"\".to_string())" +--- +Err( + [ + ScanError { + location: Location { + file: "", + offset: 0, + length: 5, + }, + kind: UnknownMetaCommand( + ".halp", + ), + }, + ], +) diff --git a/src/tokens.rs b/src/tokens.rs index 5ee06a4..0bff497 100644 --- a/src/tokens.rs +++ b/src/tokens.rs @@ -1,3 +1,5 @@ +use crate::meta_commands::MetaCommand; + #[derive(Debug, Eq, PartialEq)] pub enum TokenData { /// INSERT @@ -8,6 +10,7 @@ pub enum TokenData { Integer(i64), /// Hello World! String(String), + MetaCommand(MetaCommand), /// No file O.O? EndOfFile, } @@ -61,6 +64,7 @@ pub enum ScanErrorKind { UnexpectedChar(char), UnexpectedEndOfInput, UnknownKeyword(String), + UnknownMetaCommand(String), } #[derive(Debug, Eq, PartialEq)] @@ -105,6 +109,44 @@ impl Tokenizer { } } + fn recognize_metacommand(word: &str) -> Option { + match word.to_lowercase().as_str() { + ".exit" => Some(TokenData::MetaCommand(MetaCommand::Exit)), + _ => None, + } + } + + fn scan_meta_command(&mut self) -> Result { + let start_offset = self.offset; + let mut word = String::new(); + let mut length = 0; + if let Some(c) = self.advance() { + word.push(c); + length += 1; + } + while let Some(c) = self.peek() { + if c.is_alphabetic() || c == '_' { + word.push(c); + self.advance(); + } else { + break; + } + length += 1; + } + if let Some(meta) = Self::recognize_metacommand(&word) { + Ok(Token { + location: Location::new(self.file.clone(), start_offset, length), + data: meta, + lexeme: word, + }) + } else { + Err(ScanError { + location: Location::new(self.file.clone(), start_offset, length), + kind: ScanErrorKind::UnknownMetaCommand(word), + }) + } + } + fn scan_identifier_or_keyword(&mut self) -> Result { let start_offset = self.offset; let mut word = String::new(); @@ -149,6 +191,8 @@ impl Tokenizer { if let Some(c) = self.peek() { if Self::ident_or_keyword_start(c) { return self.scan_identifier_or_keyword(); + } else if c == '.' { + return self.scan_meta_command(); } else if c.is_whitespace() { self.advance(); } else { @@ -193,66 +237,82 @@ pub fn tokenize(input: String, file: String) -> Result, Vec".to_string())); + } + + #[test] + fn test_tokenize_unknown_meta_command() { + assert_debug_snapshot!(tokenize(".halp".to_string(), "".to_string())); + } + + #[test] + fn test_tokenizer() { + let mut scanresult = + tokenize("INSERT Select".to_string(), "src/statement.sql".to_string()).unwrap(); + scanresult.reverse(); + assert_eq!( + scanresult.pop(), + Some(Token { + location: Location::new(String::from("src/statement.sql"), 0, 6), + data: TokenData::Insert, + lexeme: String::from("INSERT"), + }) + ); + assert_eq!( + scanresult.pop(), + Some(Token { + location: Location::new(String::from("src/statement.sql"), 7, 6), + data: TokenData::Select, + lexeme: String::from("Select"), + }) + ); + assert_eq!( + scanresult.pop(), + Some(Token { + location: Location::new(String::from("src/statement.sql"), 13, 0), + data: TokenData::EndOfFile, + lexeme: String::from(""), + }) + ); + assert_eq!(scanresult.pop(), None); + assert!(scanresult.is_empty()); + } + + #[test] + fn test_tokenizer_errors() { + let mut scanerrors = tokenize("salact +".to_string(), "src/statement.sql".to_string()) + .err() + .unwrap(); + scanerrors.reverse(); + assert_eq!( + scanerrors.pop(), + Some(ScanError { + location: Location { + file: "src/statement.sql".to_string(), + offset: 0, + length: 6, + }, + kind: ScanErrorKind::UnknownKeyword("salact".to_string()), + }) + ); + assert_eq!( + scanerrors.pop(), + Some(ScanError { + location: Location { + file: "src/statement.sql".to_string(), + offset: 8, + length: 1, + }, + kind: ScanErrorKind::UnexpectedChar('+'), + }) + ); + assert!(scanerrors.is_empty()); + } } From b8ed0868cb892e61861baa2a08af23400230acee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kha=C3=AFs=20COLIN?= Date: Sun, 4 May 2025 13:36:39 +0200 Subject: [PATCH 07/10] feat(CommandParseError): add ScanError variand with Into impl --- notes.org | 5 ++++- src/command.rs | 9 +++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/notes.org b/notes.org index c440953..47adb70 100644 --- a/notes.org +++ b/notes.org @@ -145,10 +145,13 @@ CLOCK: [2025-05-04 dim. 13:32]--[2025-05-04 dim. 13:35] => 0:03 CLOCK: [2025-05-04 dim. 13:27]--[2025-05-04 dim. 13:32] => 0:05 :END: -*** TODO CommandParseError must have a ScanError variant with an Into impl +*** DONE CommandParseError must have a ScanError variant with an Into impl :PROPERTIES: :EFFORT: 10 :END: +:LOGBOOK: +CLOCK: [2025-05-04 dim. 13:35]--[2025-05-04 dim. 13:38] => 0:03 +:END: *** TODO ScanErrors must be convertible to ariadne reports :PROPERTIES: diff --git a/src/command.rs b/src/command.rs index 32e4cd0..cfbfd5e 100644 --- a/src/command.rs +++ b/src/command.rs @@ -1,5 +1,6 @@ use crate::meta_commands::{MetaCommand, MetaCommandExecuteResult, MetaCommandParseError}; use crate::statements::{Statement, StatementExecuteResult, StatementParseError}; +use crate::tokens::ScanError; #[derive(Debug)] pub enum Command { @@ -50,6 +51,7 @@ impl Command { pub enum CommandParseError { MetaCommand(MetaCommandParseError), Statement(StatementParseError), + Scan(ScanError), } impl std::fmt::Display for CommandParseError { @@ -61,6 +63,7 @@ impl std::fmt::Display for CommandParseError { CommandParseError::Statement(statement_parse_error) => { write!(f, "{statement_parse_error}") } + CommandParseError::Scan(scan_error) => write!(f, "{scan_error:?}"), } } } @@ -89,6 +92,12 @@ impl From for CommandParseError { } } +impl From for CommandParseError { + fn from(value: ScanError) -> Self { + CommandParseError::Scan(value) + } +} + impl std::str::FromStr for Command { type Err = CommandParseError; From 55b4779964b30069078a01a1b3ae83e00ff22d48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kha=C3=AFs=20COLIN?= Date: Sun, 4 May 2025 13:43:05 +0200 Subject: [PATCH 08/10] refactor(CommandParseError): remove the display implementation we want to be able to give additional arguments to the display function later, so this is needed --- notes.org | 10 +++++++++- src/command.rs | 14 +++++--------- src/error_display.rs | 2 +- src/tokens.rs | 18 ++++++++++++++++++ 4 files changed, 33 insertions(+), 11 deletions(-) diff --git a/notes.org b/notes.org index 47adb70..36210c4 100644 --- a/notes.org +++ b/notes.org @@ -158,7 +158,15 @@ CLOCK: [2025-05-04 dim. 13:35]--[2025-05-04 dim. 13:38] => 0:03 :EFFORT: 10 :END: -**** TODO Remove the CommandParseError Display implementation +**** DONE Remove the CommandParseError Display implementation +:PROPERTIES: +:EFFORT: 10 +:END: +:LOGBOOK: +CLOCK: [2025-05-04 dim. 13:38]--[2025-05-04 dim. 13:44] => 0:06 +:END: + +**** TODO implement OSDBError for ScanError :PROPERTIES: :EFFORT: 10 :END: diff --git a/src/command.rs b/src/command.rs index cfbfd5e..d016549 100644 --- a/src/command.rs +++ b/src/command.rs @@ -54,16 +54,12 @@ pub enum CommandParseError { Scan(ScanError), } -impl std::fmt::Display for CommandParseError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +impl CommandParseError { + pub(crate) fn message(&self) -> String { match self { - CommandParseError::MetaCommand(meta_command_parse_error) => { - write!(f, "{meta_command_parse_error}") - } - CommandParseError::Statement(statement_parse_error) => { - write!(f, "{statement_parse_error}") - } - CommandParseError::Scan(scan_error) => write!(f, "{scan_error:?}"), + CommandParseError::MetaCommand(x) => format!("{x}"), + CommandParseError::Statement(x) => format!("{x}"), + CommandParseError::Scan(x) => format!("{x}"), } } } diff --git a/src/error_display.rs b/src/error_display.rs index 048c1dd..f283b8d 100644 --- a/src/error_display.rs +++ b/src/error_display.rs @@ -8,7 +8,7 @@ pub trait OSDBError { impl OSDBError for CommandParseError { fn display(&self, file: &str, input: &str) { Report::build(ReportKind::Error, (file, 0..input.len() - 1)) - .with_message(format!("{self}")) + .with_message(self.message()) .with_label(Label::new((file, 0..input.len() - 1)).with_color(Color::Red)) .finish() .print((file, Source::from(input))) diff --git a/src/tokens.rs b/src/tokens.rs index 0bff497..404d10c 100644 --- a/src/tokens.rs +++ b/src/tokens.rs @@ -67,12 +67,30 @@ pub enum ScanErrorKind { UnknownMetaCommand(String), } +impl std::fmt::Display for ScanErrorKind { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + ScanErrorKind::UnexpectedChar(c) => write!(f, "unexpected char: {c:?}"), + ScanErrorKind::UnexpectedEndOfInput => write!(f, "unexpected end of input"), + ScanErrorKind::UnknownKeyword(x) => write!(f, "unknown keyword: {x:?}"), + ScanErrorKind::UnknownMetaCommand(x) => write!(f, "unknown meta-command: {x:?}"), + } + } +} + #[derive(Debug, Eq, PartialEq)] pub struct ScanError { pub location: Location, pub kind: ScanErrorKind, } +impl std::fmt::Display for ScanError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let kind = &self.kind; + write!(f, "{kind}") + } +} + impl Tokenizer { fn new(input: String, file: String) -> Self { Self { From 80cbbab6ef4f6fabeb7b71c5ebd1f2bb7c9a9cf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kha=C3=AFs=20COLIN?= Date: Sun, 4 May 2025 13:55:56 +0200 Subject: [PATCH 09/10] feat(ScanError) implement OSDBError trait --- notes.org | 7 +++++-- src/error_display.rs | 24 ++++++++++++++++++++---- src/tokens.rs | 9 +++++++++ 3 files changed, 34 insertions(+), 6 deletions(-) diff --git a/notes.org b/notes.org index 36210c4..0a535bf 100644 --- a/notes.org +++ b/notes.org @@ -153,7 +153,7 @@ CLOCK: [2025-05-04 dim. 13:27]--[2025-05-04 dim. 13:32] => 0:05 CLOCK: [2025-05-04 dim. 13:35]--[2025-05-04 dim. 13:38] => 0:03 :END: -*** TODO ScanErrors must be convertible to ariadne reports +*** DONE ScanErrors must be convertible to ariadne reports :PROPERTIES: :EFFORT: 10 :END: @@ -166,10 +166,13 @@ CLOCK: [2025-05-04 dim. 13:35]--[2025-05-04 dim. 13:38] => 0:03 CLOCK: [2025-05-04 dim. 13:38]--[2025-05-04 dim. 13:44] => 0:06 :END: -**** TODO implement OSDBError for ScanError +**** DONE implement OSDBError for ScanError :PROPERTIES: :EFFORT: 10 :END: +:LOGBOOK: +CLOCK: [2025-05-04 dim. 13:45]--[2025-05-04 dim. 13:56] => 0:11 +:END: *** TODO remove token types which are not recognized at all :PROPERTIES: diff --git a/src/error_display.rs b/src/error_display.rs index f283b8d..838acd4 100644 --- a/src/error_display.rs +++ b/src/error_display.rs @@ -1,4 +1,4 @@ -use crate::command::CommandParseError; +use crate::{command::CommandParseError, tokens::ScanError}; use ariadne::{Color, Label, Report, ReportKind, Source}; pub trait OSDBError { @@ -7,9 +7,25 @@ pub trait OSDBError { impl OSDBError for CommandParseError { fn display(&self, file: &str, input: &str) { - Report::build(ReportKind::Error, (file, 0..input.len() - 1)) - .with_message(self.message()) - .with_label(Label::new((file, 0..input.len() - 1)).with_color(Color::Red)) + if let CommandParseError::Scan(x) = self { + x.display(file, input); + } else { + Report::build(ReportKind::Error, (file, 0..input.len() - 1)) + .with_message(self.message()) + .with_label(Label::new((file, 0..input.len() - 1)).with_color(Color::Red)) + .finish() + .print((file, Source::from(input))) + .unwrap(); + } + } +} + +impl OSDBError for ScanError { + fn display(&self, file: &str, input: &str) { + let location = (file, Into::>::into(&self.location)); + Report::build(ReportKind::Error, location.clone()) + .with_message(format!("{self}")) + .with_label(Label::new(location).with_color(Color::Red)) .finish() .print((file, Source::from(input))) .unwrap(); diff --git a/src/tokens.rs b/src/tokens.rs index 404d10c..9a88df1 100644 --- a/src/tokens.rs +++ b/src/tokens.rs @@ -25,6 +25,15 @@ pub struct Location { pub length: usize, } +impl From<&Location> for std::ops::Range { + fn from(val: &Location) -> Self { + std::ops::Range { + start: val.offset, + end: val.offset + val.length, + } + } +} + impl Location { /// ``` /// use osdb::tokens::Location; From f634da331878c96262aa16350d9565c6846c5133 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kha=C3=AFs=20COLIN?= Date: Sun, 4 May 2025 13:57:23 +0200 Subject: [PATCH 10/10] refactor(TokenKind): remove unused kinds --- notes.org | 2 +- src/tokens.rs | 7 ------- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/notes.org b/notes.org index 0a535bf..e059314 100644 --- a/notes.org +++ b/notes.org @@ -174,7 +174,7 @@ CLOCK: [2025-05-04 dim. 13:38]--[2025-05-04 dim. 13:44] => 0:06 CLOCK: [2025-05-04 dim. 13:45]--[2025-05-04 dim. 13:56] => 0:11 :END: -*** TODO remove token types which are not recognized at all +*** DONE remove token types which are not recognized at all :PROPERTIES: :EFFORT: 10 :END: diff --git a/src/tokens.rs b/src/tokens.rs index 9a88df1..4ecc87d 100644 --- a/src/tokens.rs +++ b/src/tokens.rs @@ -2,16 +2,9 @@ use crate::meta_commands::MetaCommand; #[derive(Debug, Eq, PartialEq)] pub enum TokenData { - /// INSERT Insert, - /// SELECT Select, - /// 0, 1, -21635, 867463 - Integer(i64), - /// Hello World! - String(String), MetaCommand(MetaCommand), - /// No file O.O? EndOfFile, }