use dcbor::prelude::*;
use super::helpers::{
calculate_repeat_bounds, can_repeat_match, extract_capture_with_repeat,
};
use crate::pattern::{Matcher, MetaPattern, Pattern, meta::RepeatPattern};
pub trait BacktrackState<T> {
fn try_advance(&mut self, pattern_idx: usize, element_idx: usize) -> bool;
fn backtrack(&mut self);
fn is_success(
&self,
pattern_idx: usize,
element_idx: usize,
patterns_len: usize,
elements_len: usize,
) -> bool;
#[allow(dead_code)]
fn get_result(self) -> T;
}
pub struct BooleanBacktrackState;
impl BacktrackState<bool> for BooleanBacktrackState {
fn try_advance(
&mut self,
_pattern_idx: usize,
_element_idx: usize,
) -> bool {
true }
fn backtrack(&mut self) {
}
fn is_success(
&self,
pattern_idx: usize,
element_idx: usize,
patterns_len: usize,
elements_len: usize,
) -> bool {
pattern_idx >= patterns_len && element_idx >= elements_len
}
fn get_result(self) -> bool {
true }
}
pub struct AssignmentBacktrackState {
pub assignments: Vec<(usize, usize)>,
}
impl AssignmentBacktrackState {
pub fn new() -> Self { Self { assignments: Vec::new() } }
#[allow(dead_code)]
pub fn len(&self) -> usize { self.assignments.len() }
#[allow(dead_code)]
pub fn truncate(&mut self, len: usize) { self.assignments.truncate(len); }
}
impl BacktrackState<Vec<(usize, usize)>> for AssignmentBacktrackState {
fn try_advance(&mut self, pattern_idx: usize, element_idx: usize) -> bool {
self.assignments.push((pattern_idx, element_idx));
true
}
fn backtrack(&mut self) { self.assignments.pop(); }
fn is_success(
&self,
pattern_idx: usize,
element_idx: usize,
patterns_len: usize,
elements_len: usize,
) -> bool {
pattern_idx >= patterns_len && element_idx >= elements_len
}
fn get_result(self) -> Vec<(usize, usize)> { self.assignments }
}
pub struct GenericBacktracker<'a> {
patterns: &'a [Pattern],
arr: &'a [CBOR],
}
impl<'a> GenericBacktracker<'a> {
pub fn new(patterns: &'a [Pattern], arr: &'a [CBOR]) -> Self {
Self { patterns, arr }
}
pub fn backtrack<T, S: BacktrackState<T>>(
&self,
state: &mut S,
pattern_idx: usize,
element_idx: usize,
) -> bool {
if state.is_success(
pattern_idx,
element_idx,
self.patterns.len(),
self.arr.len(),
) {
return true;
}
if pattern_idx >= self.patterns.len() {
return false; }
let current_pattern = &self.patterns[pattern_idx];
match current_pattern {
Pattern::Meta(MetaPattern::Repeat(repeat_pattern)) => self
.try_repeat_backtrack(
repeat_pattern,
state,
pattern_idx,
element_idx,
),
Pattern::Meta(MetaPattern::Capture(_capture_pattern)) => {
if let Some(repeat_pattern) =
extract_capture_with_repeat(current_pattern)
{
self.try_repeat_backtrack(
repeat_pattern,
state,
pattern_idx,
element_idx,
)
} else {
if element_idx < self.arr.len() {
let element = &self.arr[element_idx];
let matches = current_pattern.matches(element);
if matches
&& state.try_advance(pattern_idx, element_idx)
{
if self.backtrack(
state,
pattern_idx + 1,
element_idx + 1,
) {
return true;
}
state.backtrack();
}
}
false
}
}
_ => {
if element_idx < self.arr.len() {
let element = &self.arr[element_idx];
let matches = current_pattern.matches(element);
if matches && state.try_advance(pattern_idx, element_idx) {
if self.backtrack(
state,
pattern_idx + 1,
element_idx + 1,
) {
return true;
}
state.backtrack();
}
}
false
}
}
}
fn try_repeat_backtrack<T, S: BacktrackState<T>>(
&self,
repeat_pattern: &RepeatPattern,
state: &mut S,
pattern_idx: usize,
element_idx: usize,
) -> bool {
let quantifier = repeat_pattern.quantifier();
let (min_count, max_count) =
calculate_repeat_bounds(quantifier, element_idx, self.arr.len());
for rep_count in (min_count..=max_count).rev() {
if element_idx + rep_count <= self.arr.len()
&& can_repeat_match(
repeat_pattern,
self.arr,
element_idx,
rep_count,
)
{
for i in 0..rep_count {
if !state.try_advance(pattern_idx, element_idx + i) {
for _ in 0..i {
state.backtrack();
}
break;
}
}
if self.backtrack(
state,
pattern_idx + 1,
element_idx + rep_count,
) {
return true;
}
for _ in 0..rep_count {
state.backtrack();
}
}
}
false
}
}