use std::collections::HashMap;
use bc_envelope::prelude::*;
use crate::pattern::{Matcher, Path, Pattern, vm::Instr};
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct OrPattern(Vec<Pattern>);
impl OrPattern {
pub fn new(patterns: Vec<Pattern>) -> Self { OrPattern(patterns) }
pub fn patterns(&self) -> &[Pattern] { &self.0 }
}
impl Matcher for OrPattern {
fn paths_with_captures(
&self,
haystack: &Envelope,
) -> (Vec<Path>, HashMap<String, Vec<Path>>) {
let paths = if self
.patterns()
.iter()
.any(|pattern| pattern.matches(haystack))
{
vec![vec![haystack.clone()]]
} else {
vec![]
};
(paths, HashMap::new())
}
fn compile(
&self,
code: &mut Vec<Instr>,
lits: &mut Vec<Pattern>,
captures: &mut Vec<String>,
) {
if self.patterns().is_empty() {
return;
}
let mut splits = Vec::new();
for _ in 0..self.patterns().len() - 1 {
splits.push(code.len());
code.push(Instr::Split { a: 0, b: 0 }); }
for (i, pattern) in self.patterns().iter().enumerate() {
let pattern_start = code.len();
pattern.compile(code, lits, captures);
let jump_past_all = code.len();
code.push(Instr::Jump(0));
if i < self.patterns().len() - 1 {
let next_pattern = code.len();
code[splits[i]] =
Instr::Split { a: pattern_start, b: next_pattern };
}
splits.push(jump_past_all);
}
let past_all = code.len();
for &jump in &splits[self.patterns().len() - 1..] {
code[jump] = Instr::Jump(past_all);
}
}
fn is_complex(&self) -> bool {
self.patterns().len() > 1
|| self.patterns().iter().any(|p| p.is_complex())
}
}
impl std::fmt::Display for OrPattern {
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_or_pattern_display() {
let pattern1 = Pattern::text("Alice");
let pattern2 = Pattern::text("Bob");
let or_pattern = OrPattern::new(vec![pattern1, pattern2]);
assert_eq!(or_pattern.to_string(), r#""Alice" | "Bob""#);
}
}