minisql/parser/src/literal.rs
Maxim Svistunov 61c0a34253 Add parsing (incl. validation)
Ensure identifiers start with alphabetical character

Rename parse_variable_name -> parse_column_name

Add DB value parsers and condition parser placeholder

Fix number parser, basic condition parser

Move select parser to select module

Add create statement parser

Move condition parser to common; add delete statement parser

Add drop statement parser

Add insert parser

Add update parser, combine operation parsers into one

Add initial validation, fix compiler warnings

Validation WIP

Allow more spaces in create statement, update TableSchema struct

Add create index parser and validator

Add todo in parse_identifier

Rework the new structure, many other changes
2024-01-26 18:20:45 +01:00

164 lines
No EOL
5.3 KiB
Rust

use minisql::type_system::{IndexableValue, Value};
use nom::{
branch::alt,
character::complete::{u64, char, digit1, none_of},
combinator::opt,
multi::many0,
sequence::{delimited, pair, preceded},
IResult, error::make_error
};
pub fn parse_db_value(input: &str) -> IResult<&str, Value> {
alt((
parse_string,
parse_number,
parse_int,
parse_uuid,
))(input)
}
pub fn parse_number(input: &str) -> IResult<&str, Value> {
// Parse the integer part
let (input, (sign, digits)) = pair(opt(char('-')), digit1)(input)?;
// Parse the fractional part
let (input, frac_part) = opt(pair(char('.'), digit1))(input)?;
match frac_part {
Some((_fsign, fdigits)) => {
// Combine integer and fractional parts
let combined_parts = format!(
"{}{}",
format!("{}{}", sign.unwrap_or('+'), digits),
format!(".{}", fdigits)
);
// Parse the combined parts as a floating-point number
let value = combined_parts.parse::<f64>()
.map_err(|_| {
nom::Err::Failure(make_error(input, nom::error::ErrorKind::Fail))
})?;
Ok((input, Value::Number(value)))
}
None => {
let value = format!("{}{}", sign.unwrap_or('+'), digits).parse::<u64>()
.map_err(|_| {
nom::Err::Failure(make_error(input, nom::error::ErrorKind::Fail))
})?;
Ok((input, Value::Indexable(IndexableValue::Int(value))))
}
}
}
pub fn parse_int(input: &str) -> IResult<&str, Value> {
u64(input).map(|(input, v)| {
(input, Value::Indexable(IndexableValue::Int(v)))
})
}
fn escape_tab(input:&str) -> IResult<&str, char> {
let (input, _) = preceded(char('\\'), char('t'))(input)?;
Ok((input, '\t'))
}
fn escape_backslash(input:&str) -> IResult<&str, char> {
let (input, _) = preceded(char('\\'), char('\\'))(input)?;
Ok((input, '\\'))
}
fn escape_newline(input:&str) -> IResult<&str, char> {
let (input, _) = preceded(char('\\'), char('n'))(input)?;
Ok((input, '\n'))
}
fn escape_carriegereturn(input:&str) -> IResult<&str, char> {
let (input, _) = preceded(char('\\'), char('r'))(input)?;
Ok((input, '\r'))
}
fn escape_doublequote(input:&str) -> IResult<&str, char> {
preceded(char('\\'), char('"'))(input)
}
pub fn parse_string(input: &str) -> IResult<&str, Value> {
// Parse the content inside the double quotes
let (input, content) = delimited(
char('"'),
many0(alt((
escape_backslash,
escape_carriegereturn,
escape_newline,
escape_doublequote,
escape_tab,
none_of(r#"\""#)
))),
char('"'),
)(input)?;
// Combine the characters into a string
let value: String = content.into_iter().collect();
Ok((input, Value::Indexable(IndexableValue::String(value))))
}
fn parse_uuid(input: &str) -> IResult<&str, Value> {
// TODO: make it actually uuid
u64(input).map(|(input, v)| {
(input, Value::Indexable(IndexableValue::Uuid(v)))
})
}
#[cfg(test)]
mod tests {
use minisql::type_system::{IndexableValue, Value};
use crate::literal::{parse_db_value, parse_string};
#[test]
fn test_string_parser() {
assert_eq!(parse_string(r#""simple""#), Ok(("", Value::Indexable(IndexableValue::String(String::from("simple"))))));
assert_eq!(parse_string(r#""\"\t\r\n\\""#), Ok(("", Value::Indexable(IndexableValue::String(String::from("\"\t\r\n\\"))))));
assert_eq!(parse_string(r#""name is \"John\".""#), Ok(("", Value::Indexable(IndexableValue::String(String::from("name is \"John\"."))))));
}
#[test]
fn test_parse_db_value() {
let (input, value) = parse_db_value("5").expect("should parse");
assert_eq!(input, "");
assert_eq!(value, Value::Indexable(IndexableValue::Int(5)));
let (input, value) = parse_db_value("5.5").expect("should parse");
assert_eq!(input, "");
assert_eq!(value, Value::Number(5.5));
let (_, _) = parse_db_value("\"STRING\"").expect("should parse");
let (input, value) = parse_db_value("\"abcdefghkjklmnopqrstuvwxyz!@#$%^&*()_+ \"").expect("should parse");
assert_eq!(input, "");
assert_eq!(value, Value::Indexable(IndexableValue::String("abcdefghkjklmnopqrstuvwxyz!@#$%^&*()_+ ".to_string())));
}
#[test]
fn test_parse_positive_float() {
assert_eq!(parse_db_value("23.213313"), Ok(("", Value::Number(23.213313))));
assert_eq!(parse_db_value("2241.9734"), Ok(("", Value::Number(2241.9734))));
}
#[test]
fn test_parse_negative_float() {
assert_eq!(parse_db_value("-9241.873654"), Ok(("", Value::Number(-9241.873654))));
assert_eq!(parse_db_value("-62625.0"), Ok(("", Value::Number(-62625.0))));
}
#[test]
fn test_parse_float_between_0_and_1() {
assert_eq!(parse_db_value("0.873654"), Ok(("", Value::Number(0.873654))));
assert_eq!(parse_db_value("0.62625"), Ok(("", Value::Number(0.62625))));
}
#[test]
fn test_parse_int() {
assert_eq!(parse_db_value("5134616"), Ok(("", Value::Indexable(IndexableValue::Int(5134616)))));
}
}