Compare commits

..

No commits in common. "f634da331878c96262aa16350d9565c6846c5133" and "32cfdfa07f6fc0ce9eeb7bdaeee917208b1fa58f" have entirely different histories.

10 changed files with 101 additions and 362 deletions

23
Cargo.lock generated
View file

@ -2,16 +2,6 @@
# 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"
@ -57,7 +47,6 @@ checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
name = "osdb"
version = "0.1.0"
dependencies = [
"ariadne",
"insta",
]
@ -67,12 +56,6 @@ 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"
@ -145,9 +128,3 @@ 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"

View file

@ -5,7 +5,6 @@ edition = "2024"
authors = ["Khaïs COLIN"]
[dependencies]
ariadne = "0.5.1"
[dev-dependencies]
insta = "1.43.1"

105
notes.org
View file

@ -1,6 +1,6 @@
#+title: Notes
* DONE show errors with ariadne
* TODO show errors with ariadne
:PROPERTIES:
:EFFORT: 10min
:END:
@ -11,38 +11,8 @@
:LOGBOOK:
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: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:
** 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:
** 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:
** 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:
** TODO OSDBError::display() should take a filename and display it alongside the error
* DONE snapshot testing
** DONE Find the snapshot testing library
@ -119,72 +89,3 @@ 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
: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:
*** 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:
*** DONE ScanErrors must be convertible to ariadne reports
:PROPERTIES:
:EFFORT: 10
:END:
**** 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:
**** 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:
*** DONE 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:

View file

@ -1,6 +1,5 @@
use crate::meta_commands::{MetaCommand, MetaCommandExecuteResult, MetaCommandParseError};
use crate::statements::{Statement, StatementExecuteResult, StatementParseError};
use crate::tokens::ScanError;
#[derive(Debug)]
pub enum Command {
@ -51,15 +50,17 @@ impl Command {
pub enum CommandParseError {
MetaCommand(MetaCommandParseError),
Statement(StatementParseError),
Scan(ScanError),
}
impl CommandParseError {
pub(crate) fn message(&self) -> String {
impl std::fmt::Display for CommandParseError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
CommandParseError::MetaCommand(x) => format!("{x}"),
CommandParseError::Statement(x) => format!("{x}"),
CommandParseError::Scan(x) => format!("{x}"),
CommandParseError::MetaCommand(meta_command_parse_error) => {
write!(f, "{meta_command_parse_error}")
}
CommandParseError::Statement(statement_parse_error) => {
write!(f, "{statement_parse_error}")
}
}
}
}
@ -88,12 +89,6 @@ impl From<StatementParseError> for CommandParseError {
}
}
impl From<ScanError> for CommandParseError {
fn from(value: ScanError) -> Self {
CommandParseError::Scan(value)
}
}
impl std::str::FromStr for Command {
type Err = CommandParseError;

View file

@ -1,33 +1,26 @@
use crate::{command::CommandParseError, tokens::ScanError};
use ariadne::{Color, Label, Report, ReportKind, Source};
use crate::{
command::CommandParseError, meta_commands::MetaCommandParseError,
statements::StatementParseError,
};
pub trait OSDBError {
fn display(&self, file: &str, input: &str);
fn display(&self);
}
impl OSDBError for MetaCommandParseError {
fn display(&self) {
println!("{self}")
}
}
impl OSDBError for StatementParseError {
fn display(&self) {
println!("{self}")
}
}
impl OSDBError for CommandParseError {
fn display(&self, file: &str, input: &str) {
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::<std::ops::Range<usize>>::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();
fn display(&self) {
println!("{self}")
}
}

View file

@ -14,7 +14,7 @@ fn main() {
break;
}
}
Err(err) => err.display("<stdin>", &input),
Err(err) => err.display(),
}
}
println!("Good-bye");

View file

@ -1,4 +1,4 @@
#[derive(Debug, Eq, PartialEq)]
#[derive(Debug)]
pub enum MetaCommand {
Exit,
}

View file

@ -1,28 +0,0 @@
---
source: src/tokens.rs
expression: "tokenize(\".exit\".to_string(), \"<stdin>\".to_string())"
---
Ok(
[
Token {
location: Location {
file: "<stdin>",
offset: 0,
length: 5,
},
data: MetaCommand(
Exit,
),
lexeme: ".exit",
},
Token {
location: Location {
file: "<stdin>",
offset: 5,
length: 0,
},
data: EndOfFile,
lexeme: "",
},
],
)

View file

@ -1,18 +0,0 @@
---
source: src/tokens.rs
expression: "tokenize(\".halp\".to_string(), \"<stdin>\".to_string())"
---
Err(
[
ScanError {
location: Location {
file: "<stdin>",
offset: 0,
length: 5,
},
kind: UnknownMetaCommand(
".halp",
),
},
],
)

View file

@ -1,10 +1,14 @@
use crate::meta_commands::MetaCommand;
#[derive(Debug, Eq, PartialEq)]
pub enum TokenData {
/// INSERT
Insert,
/// SELECT
Select,
MetaCommand(MetaCommand),
/// 0, 1, -21635, 867463
Integer(i64),
/// Hello World!
String(String),
/// No file O.O?
EndOfFile,
}
@ -18,15 +22,6 @@ pub struct Location {
pub length: usize,
}
impl From<&Location> for std::ops::Range<usize> {
fn from(val: &Location) -> Self {
std::ops::Range {
start: val.offset,
end: val.offset + val.length,
}
}
}
impl Location {
/// ```
/// use osdb::tokens::Location;
@ -66,18 +61,6 @@ pub enum ScanErrorKind {
UnexpectedChar(char),
UnexpectedEndOfInput,
UnknownKeyword(String),
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)]
@ -86,13 +69,6 @@ pub struct ScanError {
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 {
@ -129,44 +105,6 @@ impl Tokenizer {
}
}
fn recognize_metacommand(word: &str) -> Option<TokenData> {
match word.to_lowercase().as_str() {
".exit" => Some(TokenData::MetaCommand(MetaCommand::Exit)),
_ => None,
}
}
fn scan_meta_command(&mut self) -> Result<Token, ScanError> {
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<Token, ScanError> {
let start_offset = self.offset;
let mut word = String::new();
@ -211,8 +149,6 @@ 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 {
@ -257,23 +193,8 @@ pub fn tokenize(input: String, file: String) -> Result<Vec<Token>, Vec<ScanError
}
}
#[cfg(test)]
mod tests {
use super::*;
use insta::assert_debug_snapshot;
#[test]
fn test_tokenize_meta_command() {
assert_debug_snapshot!(tokenize(".exit".to_string(), "<stdin>".to_string()));
}
#[test]
fn test_tokenize_unknown_meta_command() {
assert_debug_snapshot!(tokenize(".halp".to_string(), "<stdin>".to_string()));
}
#[test]
fn test_tokenizer() {
#[test]
fn test_tokenizer() {
let mut scanresult =
tokenize("INSERT Select".to_string(), "src/statement.sql".to_string()).unwrap();
scanresult.reverse();
@ -303,10 +224,10 @@ mod tests {
);
assert_eq!(scanresult.pop(), None);
assert!(scanresult.is_empty());
}
}
#[test]
fn test_tokenizer_errors() {
#[test]
fn test_tokenizer_errors() {
let mut scanerrors = tokenize("salact +".to_string(), "src/statement.sql".to_string())
.err()
.unwrap();
@ -334,5 +255,4 @@ mod tests {
})
);
assert!(scanerrors.is_empty());
}
}