@@ -11,7 +11,9 @@ use pest::pratt_parser::Assoc as AssocNew;
1111use pest:: pratt_parser:: { Op , PrattParser } ;
1212use pest:: Parser ;
1313use regex:: Regex ;
14+ use std:: collections:: HashMap ;
1415use std:: net:: { IpAddr , Ipv4Addr , Ipv6Addr } ;
16+ use std:: rc:: Rc ;
1517
1618type ParseResult < T > = Result < T , ParseError < Rule > > ;
1719
@@ -61,12 +63,16 @@ impl ATCParser {
6163 }
6264 // matcher = { SOI ~ expression ~ EOI }
6365 #[ allow( clippy:: result_large_err) ] // it's fine as parsing is not the hot path
64- fn parse_matcher ( & mut self , source : & str ) -> ParseResult < Expression > {
66+ fn parse_matcher (
67+ & mut self ,
68+ source : & str ,
69+ regex_cache : & mut HashMap < String , Rc < Regex > > ,
70+ ) -> ParseResult < Expression > {
6571 let pairs = ATCParser :: parse ( Rule :: matcher, source) ?;
6672 let expr_pair = pairs. peek ( ) . unwrap ( ) . into_inner ( ) . peek ( ) . unwrap ( ) ;
6773 let rule = expr_pair. as_rule ( ) ;
6874 match rule {
69- Rule :: expression => parse_expression ( expr_pair, & self . pratt_parser ) ,
75+ Rule :: expression => parse_expression ( expr_pair, & self . pratt_parser , regex_cache ) ,
7076 _ => unreachable ! ( ) ,
7177 }
7278 }
@@ -204,7 +210,10 @@ fn parse_int_literal(pair: Pair<Rule>) -> ParseResult<i64> {
204210
205211// predicate = { lhs ~ binary_operator ~ rhs }
206212#[ allow( clippy:: result_large_err) ] // it's fine as parsing is not the hot path
207- fn parse_predicate ( pair : Pair < Rule > ) -> ParseResult < Predicate > {
213+ fn parse_predicate (
214+ pair : Pair < Rule > ,
215+ regex_cache : & mut HashMap < String , Rc < Regex > > ,
216+ ) -> ParseResult < Predicate > {
208217 let mut pairs = pair. into_inner ( ) ;
209218 let lhs = parse_lhs ( pairs. next ( ) . unwrap ( ) ) ?;
210219 let op = parse_binary_operator ( pairs. next ( ) . unwrap ( ) ) ;
@@ -214,23 +223,22 @@ fn parse_predicate(pair: Pair<Rule>) -> ParseResult<Predicate> {
214223 lhs,
215224 rhs : if op == BinaryOperator :: Regex {
216225 if let Value :: String ( s) = rhs {
217- let r = Regex :: new ( & s) . map_err ( |e| {
218- ParseError :: new_from_span (
219- ErrorVariant :: CustomError {
220- message : e. to_string ( ) ,
221- } ,
222- rhs_pair. as_span ( ) ,
223- )
224- } ) ?;
225-
226- Value :: Regex ( r)
226+ let regex_rc = match regex_cache. get ( & s) {
227+ Some ( stored_regex_rc) => stored_regex_rc. clone ( ) ,
228+ None => {
229+ let r = Regex :: new ( & s) . into_parse_result ( & rhs_pair) ?;
230+
231+ let rc = Rc :: new ( r) ;
232+
233+ regex_cache. insert ( s, rc. clone ( ) ) ;
234+ rc
235+ }
236+ } ;
237+
238+ Value :: Regex ( regex_rc)
227239 } else {
228- return Err ( ParseError :: new_from_span (
229- ErrorVariant :: CustomError {
230- message : "regex operator can only be used with String operands" . to_string ( ) ,
231- } ,
232- rhs_pair. as_span ( ) ,
233- ) ) ;
240+ return Err ( "regex operator can only be used with String operands" )
241+ . into_parse_result ( & rhs_pair) ;
234242 }
235243 } else {
236244 rhs
@@ -289,39 +297,53 @@ fn parse_binary_operator(pair: Pair<Rule>) -> BinaryOperator {
289297fn parse_parenthesised_expression (
290298 pair : Pair < Rule > ,
291299 pratt : & PrattParser < Rule > ,
300+ regex_cache : & mut HashMap < String , Rc < Regex > > ,
292301) -> ParseResult < Expression > {
293302 let mut pairs = pair. into_inner ( ) ;
294303 let pair = pairs. next ( ) . unwrap ( ) ;
295304 let rule = pair. as_rule ( ) ;
296305 match rule {
297- Rule :: expression => parse_expression ( pair, pratt) ,
306+ Rule :: expression => parse_expression ( pair, pratt, regex_cache ) ,
298307 Rule :: not_op => Ok ( Expression :: Logical ( Box :: new ( LogicalExpression :: Not (
299- parse_expression ( pairs. next ( ) . unwrap ( ) , pratt) ?,
308+ parse_expression ( pairs. next ( ) . unwrap ( ) , pratt, regex_cache ) ?,
300309 ) ) ) ) ,
301310 _ => unreachable ! ( ) ,
302311 }
303312}
304313
305314// term = { predicate | parenthesised_expression }
306315#[ allow( clippy:: result_large_err) ] // it's fine as parsing is not the hot path
307- fn parse_term ( pair : Pair < Rule > , pratt : & PrattParser < Rule > ) -> ParseResult < Expression > {
316+ fn parse_term (
317+ pair : Pair < Rule > ,
318+ pratt : & PrattParser < Rule > ,
319+ regex_cache : & mut HashMap < String , Rc < Regex > > ,
320+ ) -> ParseResult < Expression > {
308321 let pairs = pair. into_inner ( ) ;
309322 let inner_rule = pairs. peek ( ) . unwrap ( ) ;
310323 let rule = inner_rule. as_rule ( ) ;
311324 match rule {
312- Rule :: predicate => Ok ( Expression :: Predicate ( parse_predicate ( inner_rule) ?) ) ,
313- Rule :: parenthesised_expression => parse_parenthesised_expression ( inner_rule, pratt) ,
325+ Rule :: predicate => Ok ( Expression :: Predicate ( parse_predicate (
326+ inner_rule,
327+ regex_cache,
328+ ) ?) ) ,
329+ Rule :: parenthesised_expression => {
330+ parse_parenthesised_expression ( inner_rule, pratt, regex_cache)
331+ }
314332 _ => unreachable ! ( ) ,
315333 }
316334}
317335
318336// expression = { term ~ ( logical_operator ~ term )* }
319337#[ allow( clippy:: result_large_err) ] // it's fine as parsing is not the hot path
320- fn parse_expression ( pair : Pair < Rule > , pratt : & PrattParser < Rule > ) -> ParseResult < Expression > {
338+ fn parse_expression (
339+ pair : Pair < Rule > ,
340+ pratt : & PrattParser < Rule > ,
341+ regex_cache : & mut HashMap < String , Rc < Regex > > ,
342+ ) -> ParseResult < Expression > {
321343 let pairs = pair. into_inner ( ) ;
322344 pratt
323345 . map_primary ( |operand| match operand. as_rule ( ) {
324- Rule :: term => parse_term ( operand, pratt) ,
346+ Rule :: term => parse_term ( operand, pratt, regex_cache ) ,
325347 _ => unreachable ! ( ) ,
326348 } )
327349 . map_infix ( |lhs, op, rhs| {
@@ -335,8 +357,11 @@ fn parse_expression(pair: Pair<Rule>, pratt: &PrattParser<Rule>) -> ParseResult<
335357}
336358
337359#[ allow( clippy:: result_large_err) ] // it's fine as parsing is not the hot path
338- pub fn parse ( source : & str ) -> ParseResult < Expression > {
339- ATCParser :: new ( ) . parse_matcher ( source)
360+ pub fn parse (
361+ source : & str ,
362+ regex_cache : & mut HashMap < String , Rc < Regex > > ,
363+ ) -> ParseResult < Expression > {
364+ ATCParser :: new ( ) . parse_matcher ( source, regex_cache)
340365}
341366
342367#[ cfg( test) ]
@@ -345,16 +370,19 @@ mod tests {
345370
346371 #[ test]
347372 fn test_bad_syntax ( ) {
373+ let mut regex_cache = HashMap :: new ( ) ;
348374 assert_eq ! (
349- parse( "! a == 1" ) . unwrap_err( ) . to_string( ) ,
375+ parse( "! a == 1" , & mut regex_cache ) . unwrap_err( ) . to_string( ) ,
350376 " --> 1:1\n |\n 1 | ! a == 1\n | ^---\n |\n = expected term"
351377 ) ;
352378 assert_eq ! (
353- parse( "a == 1 || ! b == 2" ) . unwrap_err( ) . to_string( ) ,
379+ parse( "a == 1 || ! b == 2" , & mut regex_cache)
380+ . unwrap_err( )
381+ . to_string( ) ,
354382 " --> 1:11\n |\n 1 | a == 1 || ! b == 2\n | ^---\n |\n = expected term"
355383 ) ;
356384 assert_eq ! (
357- parse( "(a == 1 || b == 2) && ! c == 3" )
385+ parse( "(a == 1 || b == 2) && ! c == 3" , & mut regex_cache )
358386 . unwrap_err( )
359387 . to_string( ) ,
360388 " --> 1:23\n |\n 1 | (a == 1 || b == 2) && ! c == 3\n | ^---\n |\n = expected term"
0 commit comments