use crate::compile::pattern::sub::SubPatternCodeGen;
use crate::traits::CaseBundle;
use proc_macro2::{Span, TokenStream};
use quote::quote_spanned;
pub trait MatchArmCodeGen {
fn generate<S: SubPatternCodeGen>(span: Span, bundle: &dyn CaseBundle) -> TokenStream;
}
pub struct MatchArmCompiler;
impl MatchArmCodeGen for MatchArmCompiler {
fn generate<S: SubPatternCodeGen>(span: Span, bundle: &dyn CaseBundle) -> TokenStream {
let match_arms = bundle
.sub_pattern_groups()
.0
.iter()
.scan(0, |position, stats| {
let anonymized_sub_pattern =
S::generate(bundle.sub_pattern_at_index(stats.first()), true);
let start = *position;
let end = start + stats.size();
*position = end;
Some(quote_spanned!(span => #anonymized_sub_pattern => (#start, #end)))
});
quote_spanned!(span => #(#match_arms),*,)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::analyse::groups::{Group, SubPatternGroups};
use crate::analyse::partition::Partitioning;
use crate::analyse::strategy::Strategy;
use crate::compile::pattern::sub::SubPatternCodeGen;
use crate::parse::sub_pattern::SubPatternDefinition;
use crate::traits::{Case, SubPattern};
use proc_macro_utils::assert_tokens;
use proc_macro2::{Ident, TokenStream};
use quote::ToTokens;
fn ident(input: &str) -> Ident {
Ident::new(input, proc_macro2::Span::call_site())
}
struct MockCaseBundle {
sub_patterns: Vec<MockSubPattern>,
profile: SubPatternGroups,
}
impl CaseBundle for MockCaseBundle {
fn case(&self) -> &dyn Case {
unimplemented!()
}
fn strategy(&self) -> &Strategy {
unimplemented!()
}
fn sub_pattern_groups(&self) -> &SubPatternGroups {
&self.profile
}
fn sub_pattern_at_index(&self, index: usize) -> &dyn SubPattern {
&self.sub_patterns[index]
}
fn partitioning(&self) -> &Option<Partitioning> {
unimplemented!()
}
}
struct MockSubPattern(syn::Ident);
impl SubPattern for MockSubPattern {
fn get(&self) -> SubPatternDefinition {
unimplemented!()
}
fn get_identifier(&self) -> &Ident {
&self.0
}
}
struct MockSubPatternCodeGen;
impl SubPatternCodeGen for MockSubPatternCodeGen {
fn generate(sub_pattern: &dyn SubPattern, _anonymous: bool) -> TokenStream {
sub_pattern.get_identifier().to_token_stream()
}
}
#[test]
fn test_single() {
let profile = SubPatternGroups(vec![Group::new(0)]);
let bundle = MockCaseBundle {
sub_patterns: vec![MockSubPattern(ident("A"))],
profile,
};
let result =
MatchArmCompiler::generate::<MockSubPatternCodeGen>(Span::call_site(), &bundle);
assert_tokens!( result, {
A => (0usize, 1usize),
})
}
#[test]
fn test_multiple_complex() {
let sub_patterns = vec![
MockSubPattern(ident("A")),
MockSubPattern(ident("B")),
MockSubPattern(ident("A")),
MockSubPattern(ident("C")),
MockSubPattern(ident("B")),
MockSubPattern(ident("D")),
MockSubPattern(ident("A")),
MockSubPattern(ident("C")),
MockSubPattern(ident("E")),
MockSubPattern(ident("B")),
MockSubPattern(ident("D")),
MockSubPattern(ident("A")),
];
let groups = vec![
{
let mut g = Group::new(0);
g.push(2);
g.push(6);
g.push(11);
g
},
{
let mut g = Group::new(1);
g.push(4);
g.push(9);
g
},
{
let mut g = Group::new(3);
g.push(7);
g
},
{
let mut g = Group::new(5);
g.push(10);
g
},
Group::new(8),
];
let profile = SubPatternGroups(groups);
let bundle = MockCaseBundle {
sub_patterns,
profile,
};
let result =
MatchArmCompiler::generate::<MockSubPatternCodeGen>(Span::call_site(), &bundle);
assert_tokens!( result, {
A => (0usize, 4usize),
B => (4usize, 7usize),
C => (7usize, 9usize),
D => (9usize, 11usize),
E => (11usize, 12usize),
})
}
}