From a86c6ac8b1241abc9a3da4a0c94d0ed8fb5515bc Mon Sep 17 00:00:00 2001 From: TennyZhuang Date: Fri, 31 May 2024 16:02:26 +0800 Subject: [PATCH] refactor(parser): introduce a compactible layer for parser v2 to call v1 (#17036) Signed-off-by: TennyZhuang --- src/sqlparser/src/parser.rs | 28 +----------- src/sqlparser/src/parser_v2/compact.rs | 44 +++++++++++++++++++ src/sqlparser/src/parser_v2/expr.rs | 60 ++++++++++++++++++++++++++ src/sqlparser/src/parser_v2/mod.rs | 40 +++++------------ 4 files changed, 116 insertions(+), 56 deletions(-) create mode 100644 src/sqlparser/src/parser_v2/compact.rs create mode 100644 src/sqlparser/src/parser_v2/expr.rs diff --git a/src/sqlparser/src/parser.rs b/src/sqlparser/src/parser.rs index b1a8e2ba22b90..c80c3c145dd96 100644 --- a/src/sqlparser/src/parser.rs +++ b/src/sqlparser/src/parser.rs @@ -1004,33 +1004,7 @@ impl Parser<'_> { } pub fn parse_case_expr(&mut self) -> PResult { - let mut operand = None; - if !self.parse_keyword(Keyword::WHEN) { - operand = Some(Box::new(self.parse_expr()?)); - self.expect_keyword(Keyword::WHEN)?; - } - let mut conditions = vec![]; - let mut results = vec![]; - loop { - conditions.push(self.parse_expr()?); - self.expect_keyword(Keyword::THEN)?; - results.push(self.parse_expr()?); - if !self.parse_keyword(Keyword::WHEN) { - break; - } - } - let else_result = if self.parse_keyword(Keyword::ELSE) { - Some(Box::new(self.parse_expr()?)) - } else { - None - }; - self.expect_keyword(Keyword::END)?; - Ok(Expr::Case { - operand, - conditions, - results, - else_result, - }) + parser_v2::expr_case(self) } /// Parse a SQL CAST function e.g. `CAST(expr AS FLOAT)` diff --git a/src/sqlparser/src/parser_v2/compact.rs b/src/sqlparser/src/parser_v2/compact.rs new file mode 100644 index 0000000000000..362e2680481aa --- /dev/null +++ b/src/sqlparser/src/parser_v2/compact.rs @@ -0,0 +1,44 @@ +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Compatible layer with parser v1 + +use winnow::{PResult, Stateful}; + +use crate::parser as parser_v1; + +pub trait ParseV1 { + fn parse_v1(&mut self, f: F) -> PResult + where + for<'a> F: FnOnce(&mut parser_v1::Parser<'a>) -> PResult; +} + +impl<'a> ParseV1 for parser_v1::Parser<'a> { + fn parse_v1(&mut self, f: F) -> PResult + where + F: FnOnce(&mut parser_v1::Parser<'a>) -> PResult, + { + f(self) + } +} + +impl ParseV1 for Stateful +where + S: ParseV1, +{ + fn parse_v1(&mut self, f: F) -> PResult + where + for<'a> F: FnOnce(&mut parser_v1::Parser<'a>) -> PResult, + { + self.input.parse_v1(f) + } +} diff --git a/src/sqlparser/src/parser_v2/expr.rs b/src/sqlparser/src/parser_v2/expr.rs new file mode 100644 index 0000000000000..7447984d7caf0 --- /dev/null +++ b/src/sqlparser/src/parser_v2/expr.rs @@ -0,0 +1,60 @@ +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +use winnow::combinator::{cut_err, opt, preceded, repeat, trace}; +use winnow::{PResult, Parser}; + +use super::TokenStream; +use crate::ast::Expr; +use crate::keywords::Keyword; + +fn expr(input: &mut S) -> PResult +where + S: TokenStream, +{ + // TODO: implement this function using combinator style. + trace("expr", |input: &mut S| { + input.parse_v1(|parser| parser.parse_expr()) + }) + .parse_next(input) +} + +pub fn expr_case(input: &mut S) -> PResult +where + S: TokenStream, +{ + let parse = ( + opt(expr), + repeat( + 1.., + ( + Keyword::WHEN, + cut_err(expr), + cut_err(Keyword::THEN), + cut_err(expr), + ), + ), + opt(preceded(Keyword::ELSE, cut_err(expr))), + cut_err(Keyword::END), + ) + .map(|(operand, branches, else_result, _)| { + let branches: Vec<_> = branches; + let (conditions, results) = branches.into_iter().map(|(_, c, _, t)| (c, t)).unzip(); + Expr::Case { + operand: operand.map(Box::new), + conditions, + results, + else_result: else_result.map(Box::new), + } + }); + + trace("expr_case", parse).parse_next(input) +} diff --git a/src/sqlparser/src/parser_v2/mod.rs b/src/sqlparser/src/parser_v2/mod.rs index 1025126afd035..366b17fb23bf5 100644 --- a/src/sqlparser/src/parser_v2/mod.rs +++ b/src/sqlparser/src/parser_v2/mod.rs @@ -20,19 +20,28 @@ use crate::ast::{Ident, ObjectName}; use crate::keywords::{self, Keyword}; use crate::tokenizer::{Token, TokenWithLocation}; +mod compact; mod data_type; +mod expr; mod impl_; mod number; pub(crate) use data_type::*; +pub(crate) use expr::*; pub(crate) use number::*; /// Bundle trait requirements from winnow, so that we don't need to write them everywhere. /// /// All combinators should accept a generic `S` that implements `TokenStream`. -pub trait TokenStream: Stream + StreamIsPartial + Default {} +pub trait TokenStream: + Stream + StreamIsPartial + Default + compact::ParseV1 +{ +} -impl TokenStream for S where S: Stream + StreamIsPartial + Default {} +impl TokenStream for S where + S: Stream + StreamIsPartial + Default + compact::ParseV1 +{ +} /// Consume any token. /// @@ -158,30 +167,3 @@ pub trait ParserExt: Parser { } impl ParserExt for T where T: Parser {} - -#[cfg(test)] -mod tests { - use super::*; - use crate::tokenizer::Tokenizer; - - #[test] - fn test_basic() { - let input = "SELECT 1"; - let tokens = Tokenizer::new(input).tokenize_with_location().unwrap(); - Token::make_keyword("SELECT") - .parse_next(&mut &tokens[..]) - .unwrap(); - } - - #[test] - fn test_stateful() { - let input = "SELECT 1"; - let tokens = Tokenizer::new(input).tokenize_with_location().unwrap(); - with_state(|input: &mut Stateful<_, usize>| -> PResult<()> { - input.state += 1; - Token::make_keyword("SELECT").void().parse_next(input) - }) - .parse_next(&mut &tokens[..]) - .unwrap(); - } -}