use minisql::type_system::Value; use nom::{ branch::alt, character::complete::{char, digit1, none_of, u64}, combinator::opt, error::make_error, multi::many0, sequence::{delimited, pair, preceded}, IResult, }; 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!("{}{}.{}", sign.unwrap_or('+'), digits, fdigits); // Parse the combined parts as a floating-point number let value = combined_parts .parse::() .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::() .map_err(|_| nom::Err::Failure(make_error(input, nom::error::ErrorKind::Fail)))?; Ok((input, Value::Int(value))) } } } pub fn parse_int(input: &str) -> IResult<&str, Value> { u64(input).map(|(input, v)| (input, Value::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::String(value))) } pub fn parse_uuid(input: &str) -> IResult<&str, Value> { let (input, value) = pair(char('u'), u64)(input) .map(|(input, (_, v))| (input, Value::Uuid(v)))?; Ok((input, value)) } #[cfg(test)] mod tests { use crate::parsing::literal::{parse_db_value, parse_string, parse_uuid}; use minisql::type_system::Value; #[test] fn test_string_parser() { assert_eq!( parse_string(r#""simple""#), Ok(( "", Value::String(String::from("simple")) )) ); assert_eq!( parse_string(r#""\"\t\r\n\\""#), Ok(( "", Value::String(String::from("\"\t\r\n\\")) )) ); assert_eq!( parse_string(r#""name is \"John\".""#), Ok(( "", Value::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::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::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::Int(5134616))) ); } #[test] fn test_parse_uuid() { assert_eq!( parse_uuid("u131515"), Ok(("", Value::Uuid(131515))) ) } }