use self::xmacro::Definition;
use self::xtoken::XToken;
#[allow(clippy::wildcard_imports)]
use crate::*;
use std::collections::HashMap;
use std::vec::IntoIter;
use crate::xmacro::Definitions;
#[derive(Debug)]
pub(crate) struct ExpansionGroup {
current_index: usize,
len: usize,
}
pub(crate) fn expand_tokens(
output: &mut Vec<XToken>,
input: &[XToken],
definitions: &[Definition],
indices: &[usize],
groups: &[ExpansionGroup],
) {
let mut token_iter = input.iter();
let definition_at =
|index: usize| definitions[index].def[groups[indices[index]].current_index].clone();
loop {
match token_iter.next() {
Some(XToken::IndexedSubstitution(_, index)) => {
output.push(XToken::TokenStream(definition_at(*index)));
}
Some(XToken::IndexOfIndexed(_, index)) => {
output.push(XToken::TokenTree(TokenTree::Literal(
Literal::usize_unsuffixed(groups[indices[*index]].current_index),
)));
}
Some(XToken::LengthOfIndexed(_, index)) => {
output.push(XToken::TokenTree(TokenTree::Literal(
Literal::usize_unsuffixed(definitions[*index].def.len()),
)));
}
Some(XToken::EscapedDollar(_)) => {
output.push(XToken::TokenTree(PunctAlone::<'$'>::new().into()));
}
Some(XToken::BreakSemicolon(_)) => {
output.push(XToken::TokenTree(PunctAlone::<';'>::new().into()));
}
Some(XToken::BreakPunct(p, _)) => {
output.push(XToken::TokenTree(TokenTree::Punct(Punct::new(
p.as_char(),
Spacing::Alone,
))));
}
Some(XToken::ConcatIdent(_, ident, _, def)) => {
let Either::First(index) = def else {
unreachable!()
};
let s = ident.to_string() + definition_at(*index).to_string().as_str();
output.push(XToken::TokenTree(TokenTree::Ident(Ident::new(
&s,
ident.span(),
))));
}
Some(XToken::XGroup(group)) => {
let mut expanded = Vec::<XToken>::new();
expand_tokens(&mut expanded, &group.content, definitions, indices, groups);
output.push(XToken::XGroup(GroupContaining {
delimiter: group.delimiter,
content: expanded,
}));
}
Some(XToken::TokenTree(tt)) => {
output.push(XToken::TokenTree(tt.clone()));
}
Some(XToken::TokenStream(ts)) => {
output.push(XToken::TokenStream(ts.clone()));
}
None | Some(XToken::EndOfStream(_)) => break,
Some(
XToken::Scope(..)
| XToken::Definition(..)
| XToken::DefinitionError(..)
| XToken::Directive(..)
| XToken::LengthOfNamed(..)
| XToken::NamedSubstitution(..)
| XToken::IndexOfNamed(..)
| XToken::Group(_),
) => unreachable!(),
}
}
}
impl XMacro<'_> {
pub fn expand(self) -> IntoIter<XToken> {
let XMacro {
xtokens,
definitions: Definitions { definitions, .. },
..
} = self;
let mut groups: Vec<ExpansionGroup> = Vec::new();
let mut group_lookup: HashMap<u32, usize> = HashMap::new();
let mut indices: Vec<usize> = Vec::new();
for def in &definitions {
if def.do_expand {
let len = groups.len();
let group_index = *group_lookup.entry(def.group_id).or_insert(len);
if group_index == len {
groups.push(ExpansionGroup {
current_index: 0,
len: def.def.len(),
});
}
indices.push(group_lookup[&def.group_id]);
} else {
indices.push(usize::MAX);
}
}
#[allow(clippy::items_after_statements)]
fn permute_group_indices(group_indices: &mut [ExpansionGroup]) -> bool {
for i in (0..group_indices.len()).rev() {
group_indices[i].current_index += 1;
if group_indices[i].current_index < group_indices[i].len {
return true;
}
group_indices[i].current_index = 0;
}
false
}
let mut output = Vec::new();
while {
expand_tokens(
&mut output,
xtokens.as_slice(),
&definitions,
indices.as_slice(),
groups.as_slice(),
);
permute_group_indices(groups.as_mut_slice())
} {}
output.into_iter()
}
}