@@ -447,6 +447,230 @@ constexpr CTRE_FORCE_INLINE R evaluate(const Iterator begin, Iterator current, c
447
447
// property matching
448
448
449
449
450
+ // pattern analysis - returns the minimum and maximum # of characters in order for a regex to match a string
451
+ // -1 is considered INF, -2 is finite (but perhaps too large to store), all other values are exact counts
452
+ constexpr CTRE_FORCE_INLINE size_t saturate_limit (const size_t & lhs, const size_t & rhs) {
453
+ const constexpr size_t inf = size_t { 0 } -1 ;
454
+ const constexpr size_t lim = size_t { 0 } -2 ;
455
+ size_t ret = inf;
456
+ if (lhs == inf || rhs == inf) {
457
+ return ret;
458
+ } else {
459
+ ret = lhs + rhs;
460
+ ret = ret < lhs ? lim : ret == inf ? lim : ret;
461
+ }
462
+ return ret;
463
+ }
464
+
465
+ constexpr CTRE_FORCE_INLINE size_t mult_saturate_limit (const size_t & lhs, const size_t & rhs) {
466
+ const constexpr size_t inf = size_t { 0 } -1 ;
467
+ const constexpr size_t lim = size_t { 0 } -2 ;
468
+ size_t ret = inf;
469
+ if (lhs == inf || rhs == inf) {
470
+ return ret;
471
+ } else if (lhs == 0 || rhs == 0 ) {
472
+ return ret = 0 ;
473
+ } else {
474
+ if (lhs > (SIZE_MAX / rhs))
475
+ return ret = lim;
476
+ ret = lhs * rhs;
477
+ ret = ret == inf ? lim : ret;
478
+ return ret;
479
+ }
480
+ }
481
+ // a custom std::pair to overload some handy operations that we'll perform w/ a fold
482
+ struct analysis_results : std::pair<size_t , size_t > {
483
+ constexpr inline CTRE_FORCE_INLINE operator bool () const noexcept {
484
+ return first;
485
+ }
486
+ constexpr auto CTRE_FORCE_INLINE operator +(analysis_results other) const noexcept {
487
+ return analysis_results{std::make_pair (
488
+ saturate_limit (first, other.first ),
489
+ saturate_limit (second, other.second )
490
+ )};
491
+ }
492
+ constexpr auto CTRE_FORCE_INLINE operator ||(analysis_results other) const noexcept {
493
+ return analysis_results{std::make_pair (
494
+ std::min (first, other.first ),
495
+ std::max (second, other.second )
496
+ )};
497
+ }
498
+ };
499
+
500
+ template <typename Pattern>
501
+ static constexpr auto trampoline_analysis (Pattern) noexcept ;
502
+
503
+ template <typename ... Patterns>
504
+ static constexpr auto trampoline_analysis (ctll::list<Patterns...>) noexcept ;
505
+
506
+ template <typename T, typename R>
507
+ static constexpr auto trampoline_analysis (T, R captures) noexcept ;
508
+
509
+ // processing for each type
510
+
511
+ // repeat
512
+ template <size_t A, size_t B, typename R, typename ... Content>
513
+ static constexpr auto _analyze (repeat<A,B,Content...>, R captures) noexcept {
514
+ analysis_results ret{ std::make_pair (0ULL , 0ULL ) };
515
+ if constexpr (sizeof ...(Content)) {
516
+ ret = trampoline_analysis (ctll::list<Content...>(), captures);
517
+ ret.first = mult_saturate_limit (ret.first , A);
518
+ ret.second = mult_saturate_limit (ret.second , B);
519
+ }
520
+ return ret;
521
+ }
522
+
523
+ // note: all * ? + operations are specialized variations of repeat {A,B}
524
+ // lazy_repeat
525
+ template <size_t A, size_t B, typename R, typename ... Content>
526
+ static constexpr auto _analyze (lazy_repeat<A, B, Content...>, R captures) noexcept {
527
+ return _analyze (repeat<A, B, Content...>(), captures);
528
+ }
529
+
530
+ // possessive_repeat
531
+ template <size_t A, size_t B, typename R, typename ... Content>
532
+ static constexpr auto _analyze (possessive_repeat<A, B, Content...>, R captures) noexcept {
533
+ return _analyze (repeat<A, B, Content...>(), captures);
534
+ }
535
+
536
+ // star
537
+ template <typename R, typename ... Content>
538
+ static constexpr auto _analyze (star<Content...>, R captures) noexcept {
539
+ return _analyze (repeat<0ULL , ~(0ULL ), Content...>(), captures);
540
+ }
541
+
542
+ // lazy_star
543
+ template <typename R, typename ... Content>
544
+ static constexpr auto _analyze (lazy_star<Content...>, R captures) noexcept {
545
+ return _analyze (repeat<0ULL , ~(0ULL ), Content...>(), captures);
546
+ }
547
+
548
+ // possessive_star
549
+ template <typename R, typename ... Content>
550
+ static constexpr auto _analyze (possessive_star<Content...>, R captures) noexcept {
551
+ return _analyze (repeat<0ULL , ~(0ULL ), Content...>(), captures);
552
+ }
553
+
554
+ // optional
555
+ template <typename R, typename ... Content>
556
+ static constexpr auto _analyze (optional<Content...>, R captures) noexcept {
557
+ return _analyze (repeat<0ULL , 1ULL , Content...>(), captures);
558
+ }
559
+
560
+ // lazy_optional
561
+ template <typename R, typename ... Content>
562
+ static constexpr auto _analyze (lazy_optional<Content...>, R captures) noexcept {
563
+ return _analyze (repeat<0ULL , 1ULL , Content...>(), captures);
564
+ }
565
+
566
+ // back_reference
567
+ template <size_t Id, typename R>
568
+ static constexpr auto _analyze (back_reference<Id>, R captures) noexcept {
569
+ const auto ref = captures.template get <Id>();
570
+ analysis_results ret{ std::make_pair (0ULL , 0ULL ) };
571
+ if constexpr (size (ref.get_expression ())) {
572
+ ret = trampoline_analysis (ref.get_expression (), captures);
573
+ }
574
+ return ret;
575
+ }
576
+
577
+ // back_reference_with_name
578
+ template <typename Name, typename R>
579
+ static constexpr auto _analyze (back_reference_with_name<Name>, R captures) noexcept {
580
+ const auto ref = captures.template get <Name>();
581
+ analysis_results ret{ std::make_pair (0ULL , 0ULL ) };
582
+ if constexpr (size (ref.get_expression ())) {
583
+ ret = trampoline_analysis (ref.get_expression (), captures);
584
+ }
585
+ return ret;
586
+ }
587
+
588
+ // select, this is specialized, we need to take the minimum of all minimums and maximum of all maximums
589
+ template <typename R, typename ... Content>
590
+ static constexpr auto _analyze (select <Content...>, R captures) noexcept {
591
+ analysis_results ret = trampoline_select_analysis (ctll::list<Content...>(), captures);
592
+ return ret;
593
+ }
594
+
595
+ // character, any character contributes exactly one to both counts
596
+ template <auto C, typename R>
597
+ static constexpr auto _analyze (character<C>, R captures) noexcept {
598
+ analysis_results ret{ std::make_pair (1ULL , 1ULL ) };
599
+ return ret;
600
+ }
601
+
602
+ // strings, any string contributes the # of characters it contains (if we have an empty string that'll be 0)
603
+ template <auto ... Str, typename R>
604
+ static constexpr auto _analyze (string<Str...>, R captures) noexcept {
605
+ analysis_results ret{ std::make_pair (sizeof ...(Str), sizeof ...(Str)) };
606
+ return ret;
607
+ }
608
+
609
+ // we'll process anything that has contents as a regex
610
+ // ctll::list
611
+ template <typename R, typename ... Content>
612
+ static constexpr auto _analyze (ctll::list<Content...>,R captures) noexcept {
613
+ analysis_results ret = trampoline_analysis (ctll::list<Content...>(), captures);
614
+ return ret;
615
+ }
616
+
617
+ // sequence
618
+ template <typename R, typename ... Content>
619
+ static constexpr auto _analyze (sequence<Content...>, R captures) noexcept {
620
+ analysis_results ret = trampoline_analysis (ctll::list<Content...>(), captures);
621
+ return ret;
622
+ }
623
+
624
+ // capture
625
+ template <size_t Id, typename R, typename ... Content>
626
+ static constexpr auto _analyze (capture<Id, Content...>, R captures) noexcept {
627
+ analysis_results ret = trampoline_analysis (ctll::list<Content...>(), captures);
628
+ return ret;
629
+ }
630
+
631
+ // capture_with_name
632
+ template <size_t Id, typename Name, typename R, typename ... Content>
633
+ static constexpr auto _analyze (capture_with_name<Id, Name, Content...>, R captures) noexcept {
634
+ analysis_results ret = trampoline_analysis (ctll::list<Content...>(), captures);
635
+ return ret;
636
+ }
637
+
638
+ // everything else, anything we haven't matched already isn't supported and will contribute 0
639
+ template <typename T, typename R>
640
+ static constexpr auto _analyze (T, R captures) noexcept {
641
+ analysis_results ret{ std::make_pair (0ULL , 0ULL ) };
642
+ return ret;
643
+ }
644
+ // note: ctll::list wraps patterns just like sequences, we'll treat anything that looks like a regex w/ ctll::list
645
+ template <typename ... Patterns, typename R>
646
+ static constexpr auto trampoline_analysis (ctll::list<Patterns...>, R captures) noexcept {
647
+ // fold, for every argument in a ctll::list, calculate its contribution to the limits
648
+ auto r = ((_analyze (Patterns (), captures)) + ...);
649
+ // note any reordering of parameters will result in the same limits
650
+ return r;
651
+ }
652
+
653
+ template <typename ... Patterns, typename R>
654
+ static constexpr auto trampoline_select_analysis (ctll::list<Patterns...>, R captures) noexcept {
655
+ // fold, each argument in a selection of regexes we take the minimum and maximum of all values
656
+ auto r = ((trampoline_analysis (Patterns (), captures)) || ...);
657
+ // note again, order is unimportant
658
+ return r;
659
+ }
660
+
661
+ template <typename ... Patterns>
662
+ static constexpr auto pattern_analysis (ctll::list<Patterns...>) noexcept {
663
+ using return_type = decltype (regex_results (std::declval<std::basic_string_view<char >::iterator>(), find_captures (pattern)));
664
+ return trampoline_analysis (ctll::list<Patterns...>(), return_type{});
665
+ }
666
+
667
+ template <typename Pattern = empty>
668
+ static constexpr auto pattern_analysis (Pattern pattern = {}) noexcept {
669
+ using return_type = decltype (regex_results (std::declval<std::basic_string_view<char >::iterator>(), find_captures (pattern)));
670
+ return trampoline_analysis (ctll::list<Pattern>(), return_type{});
671
+ }
672
+
673
+
450
674
}
451
675
452
676
#endif
0 commit comments