use std::collections::HashMap;
use bc_envelope::prelude::*;
use crate::pattern::{Matcher, Path, Pattern, vm::Instr};
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct TraversePattern {
first: Box<Pattern>,
rest: Option<Box<TraversePattern>>,
}
impl TraversePattern {
pub fn new(patterns: Vec<Pattern>) -> Self {
let mut iter = patterns.into_iter();
let first_pat = iter
.next()
.unwrap_or_else(|| Pattern::not_matching(Pattern::any()));
let rest_patterns: Vec<Pattern> = iter.collect();
let rest = if rest_patterns.is_empty() {
None
} else {
Some(Box::new(TraversePattern::new(rest_patterns)))
};
TraversePattern { first: Box::new(first_pat), rest }
}
pub fn patterns(&self) -> Vec<Pattern> {
let mut result = vec![*self.first.clone()];
if let Some(rest) = &self.rest {
result.extend(rest.patterns());
}
result
}
}
impl Matcher for TraversePattern {
fn paths_with_captures(
&self,
haystack: &Envelope,
) -> (Vec<Path>, HashMap<String, Vec<Path>>) {
let paths = {
let head_paths = self.first.paths(haystack);
if let Some(rest_seq) = &self.rest {
let mut result = Vec::new();
for path in head_paths {
if let Some(last_env) = path.last().cloned() {
for tail_path in rest_seq.paths(&last_env) {
let mut combined = path.clone();
combined.extend(tail_path);
result.push(combined);
}
}
}
result
} else {
head_paths
}
};
(paths, HashMap::new())
}
fn compile(
&self,
code: &mut Vec<Instr>,
lits: &mut Vec<Pattern>,
captures: &mut Vec<String>,
) {
self.first.compile(code, lits, captures);
if let Some(rest) = &self.rest {
code.push(Instr::ExtendTraversal);
rest.compile(code, lits, captures);
code.push(Instr::CombineTraversal);
}
}
fn is_complex(&self) -> bool {
self.first.is_complex() || self.rest.is_some()
}
}
impl std::fmt::Display for TraversePattern {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
self.patterns()
.iter()
.map(|p| p.to_string())
.collect::<Vec<_>>()
.join(" -> ")
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_traversal_pattern_display() {
let pattern1 = Pattern::wrapped();
let pattern2 = Pattern::wrapped();
let traversal_pattern = TraversePattern::new(vec![pattern1, pattern2]);
assert_eq!(traversal_pattern.to_string(), "wrapped -> wrapped");
}
}