diff --git a/lib/src/sql/comment.rs b/lib/src/sql/comment.rs index 4afe8e6f..4f1032d6 100644 --- a/lib/src/sql/comment.rs +++ b/lib/src/sql/comment.rs @@ -24,17 +24,7 @@ pub fn comment(i: &str) -> IResult<&str, ()> { Ok((i, ())) } -fn blank(i: &str) -> IResult<&str, ()> { - let (i, _) = multispace0(i)?; - Ok((i, ())) -} - -fn space(i: &str) -> IResult<&str, ()> { - let (i, _) = multispace1(i)?; - Ok((i, ())) -} - -fn block(i: &str) -> IResult<&str, ()> { +pub fn block(i: &str) -> IResult<&str, ()> { let (i, _) = multispace0(i)?; let (i, _) = char('/')(i)?; let (i, _) = char('*')(i)?; @@ -45,7 +35,7 @@ fn block(i: &str) -> IResult<&str, ()> { Ok((i, ())) } -fn slash(i: &str) -> IResult<&str, ()> { +pub fn slash(i: &str) -> IResult<&str, ()> { let (i, _) = multispace0(i)?; let (i, _) = char('/')(i)?; let (i, _) = char('/')(i)?; @@ -53,7 +43,7 @@ fn slash(i: &str) -> IResult<&str, ()> { Ok((i, ())) } -fn dash(i: &str) -> IResult<&str, ()> { +pub fn dash(i: &str) -> IResult<&str, ()> { let (i, _) = multispace0(i)?; let (i, _) = char('-')(i)?; let (i, _) = char('-')(i)?; @@ -61,9 +51,19 @@ fn dash(i: &str) -> IResult<&str, ()> { Ok((i, ())) } -fn hash(i: &str) -> IResult<&str, ()> { +pub fn hash(i: &str) -> IResult<&str, ()> { let (i, _) = multispace0(i)?; let (i, _) = char('#')(i)?; let (i, _) = not_line_ending(i)?; Ok((i, ())) } + +fn blank(i: &str) -> IResult<&str, ()> { + let (i, _) = multispace0(i)?; + Ok((i, ())) +} + +fn space(i: &str) -> IResult<&str, ()> { + let (i, _) = multispace1(i)?; + Ok((i, ())) +} diff --git a/lib/src/sql/script.rs b/lib/src/sql/script.rs index 437d76e5..0ecf68f5 100644 --- a/lib/src/sql/script.rs +++ b/lib/src/sql/script.rs @@ -1,27 +1,31 @@ +use crate::sql::comment::{block, slash}; use crate::sql::error::IResult; use nom::branch::alt; use nom::bytes::complete::escaped; use nom::bytes::complete::is_not; use nom::bytes::complete::tag; -use nom::character::complete::one_of; +use nom::character::complete::char; +use nom::character::complete::multispace0; use nom::combinator::recognize; +use nom::multi::many0; use nom::multi::many1; +use nom::sequence::delimited; use serde::{Deserialize, Serialize}; use std::fmt::{self, Display, Formatter}; use std::ops::Deref; use std::str; -const SINGLE: &str = r#"'"#; +const SINGLE: char = '\''; const SINGLE_ESC: &str = r#"\'"#; -const DOUBLE: &str = r#"""#; +const DOUBLE: char = '"'; const DOUBLE_ESC: &str = r#"\""#; -const BACKTICK: &str = r#"`"#; +const BACKTICK: char = '`'; const BACKTICK_ESC: &str = r#"\`"#; -const OBJECT_BEG: &str = "{"; -const OBJECT_END: &str = "}"; +const OBJECT_BEG: char = '{'; +const OBJECT_END: char = '}'; #[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)] pub struct Script(pub String); @@ -52,44 +56,67 @@ impl Display for Script { } pub fn script(i: &str) -> IResult<&str, Script> { - let (i, v) = recognize(script_raw)(i)?; + let (i, v) = script_raw(i)?; Ok((i, Script(String::from(v)))) } fn script_raw(i: &str) -> IResult<&str, &str> { - recognize(many1(alt((char_any, char_object, string_single, string_double, string_backtick))))(i) + recognize(many0(alt(( + script_comment, + script_object, + script_string, + script_maths, + script_other, + ))))(i) } -fn char_any(i: &str) -> IResult<&str, &str> { - is_not("{}'`\"")(i) +fn script_maths(i: &str) -> IResult<&str, &str> { + recognize(tag("/"))(i) } -fn char_object(i: &str) -> IResult<&str, &str> { - let (i, _) = tag(OBJECT_BEG)(i)?; - let (i, v) = script_raw(i)?; - let (i, _) = tag(OBJECT_END)(i)?; - Ok((i, v)) +fn script_other(i: &str) -> IResult<&str, &str> { + recognize(many1(is_not("/{}`'\"")))(i) } -fn string_single(i: &str) -> IResult<&str, &str> { - let (i, _) = tag(SINGLE)(i)?; - let (i, v) = alt((escaped(is_not(SINGLE_ESC), '\\', one_of(SINGLE)), tag("")))(i)?; - let (i, _) = tag(SINGLE)(i)?; - Ok((i, v)) +fn script_comment(i: &str) -> IResult<&str, &str> { + recognize(delimited(multispace0, many1(alt((block, slash))), multispace0))(i) } -fn string_double(i: &str) -> IResult<&str, &str> { - let (i, _) = tag(DOUBLE)(i)?; - let (i, v) = alt((escaped(is_not(DOUBLE_ESC), '\\', one_of(DOUBLE)), tag("")))(i)?; - let (i, _) = tag(DOUBLE)(i)?; - Ok((i, v)) +fn script_object(i: &str) -> IResult<&str, &str> { + recognize(delimited(char(OBJECT_BEG), script_raw, char(OBJECT_END)))(i) } -fn string_backtick(i: &str) -> IResult<&str, &str> { - let (i, _) = tag(BACKTICK)(i)?; - let (i, v) = alt((escaped(is_not(BACKTICK_ESC), '\\', one_of(BACKTICK)), tag("")))(i)?; - let (i, _) = tag(BACKTICK)(i)?; - Ok((i, v)) +fn script_string(i: &str) -> IResult<&str, &str> { + recognize(alt(( + |i| { + let (i, _) = char(SINGLE)(i)?; + let (i, _) = char(SINGLE)(i)?; + Ok((i, "")) + }, + |i| { + let (i, _) = char(DOUBLE)(i)?; + let (i, _) = char(DOUBLE)(i)?; + Ok((i, "")) + }, + |i| { + let (i, _) = char(SINGLE)(i)?; + let (i, v) = escaped(is_not(SINGLE_ESC), '\\', char(SINGLE))(i)?; + let (i, _) = char(SINGLE)(i)?; + Ok((i, v)) + }, + |i| { + let (i, _) = char(DOUBLE)(i)?; + let (i, v) = escaped(is_not(DOUBLE_ESC), '\\', char(DOUBLE))(i)?; + let (i, _) = char(DOUBLE)(i)?; + Ok((i, v)) + }, + |i| { + let (i, _) = char(BACKTICK)(i)?; + let (i, v) = escaped(is_not(BACKTICK_ESC), '\\', char(BACKTICK))(i)?; + let (i, _) = char(BACKTICK)(i)?; + Ok((i, v)) + }, + )))(i) } #[cfg(test)] @@ -150,4 +177,34 @@ mod tests { ) ); } + + #[test] + fn script_advanced() { + let sql = r#" + // { + // } + // {} + // { } + /* { */ + /* } */ + /* {} */ + /* { } */ + /* {{{ $ }} */ + /* /* /* /* */ + let x = {}; + let x = { }; + let x = '{'; + let x = "{"; + let x = '}'; + let x = "}"; + let x = '} } { {'; + let x = 3 / 4 * 2; + let x = /* something */ 45 + 2; + "#; + let res = script(sql); + assert!(res.is_ok()); + let out = res.unwrap().1; + assert_eq!(sql, format!("{}", out)); + assert_eq!(out, Script::from(sql)); + } }