use crate::lexer::{Span, Token, TokenKind};
use super::hygiene::{HygieneContext, SyntaxContext};
use super::{
match_macro_pattern, tokens_to_tree, Binding, BindingValue, Bindings, ExpansionElement,
MacroContext, MacroError, MacroExpansion, MacroResult, RepetitionKind, TokenTree,
};
pub struct MacroExpander<'ctx> {
ctx: &'ctx MacroContext,
hygiene: HygieneContext,
depth: u32,
max_depth: u32,
}
impl<'ctx> MacroExpander<'ctx> {
pub fn new(ctx: &'ctx MacroContext) -> Self {
Self {
ctx,
hygiene: HygieneContext::new(),
depth: 0,
max_depth: 128,
}
}
pub fn with_max_depth(mut self, max_depth: u32) -> Self {
self.max_depth = max_depth;
self
}
pub fn expand(
&mut self,
name: &str,
input: &[Token],
span: Span,
) -> MacroResult<Vec<TokenTree>> {
if self.depth >= self.max_depth {
return Err(MacroError::RecursionLimit {
limit: self.max_depth,
});
}
let macro_def = self
.ctx
.lookup_macro(name)
.ok_or_else(|| MacroError::MacroNotFound {
name: name.to_string(),
})?;
let input_trees = tokens_to_tree(input);
for rule in ¯o_def.rules {
if let Ok(bindings) = match_macro_pattern(&rule.pattern, &input_trees) {
let syntax_ctx = self.hygiene.fresh_context(span);
self.depth += 1;
let result = self.expand_template(&rule.expansion, &bindings, syntax_ctx);
self.depth -= 1;
return result;
}
}
Err(MacroError::NoMatchingRule {
name: name.to_string(),
})
}
fn expand_template(
&mut self,
template: &MacroExpansion,
bindings: &Bindings,
syntax_ctx: SyntaxContext,
) -> MacroResult<Vec<TokenTree>> {
let mut result = Vec::new();
for element in &template.elements {
let expanded = self.expand_element(element, bindings, syntax_ctx)?;
result.extend(expanded);
}
Ok(result)
}
fn expand_element(
&mut self,
element: &ExpansionElement,
bindings: &Bindings,
syntax_ctx: SyntaxContext,
) -> MacroResult<Vec<TokenTree>> {
match element {
ExpansionElement::Token(kind, span) => {
let token = Token {
kind: kind.clone(),
span: self.hygiene.apply_context(*span, syntax_ctx),
};
Ok(vec![TokenTree::Token(token)])
}
ExpansionElement::MetaVar(name) => self.expand_metavar(name, bindings),
ExpansionElement::Repetition {
elements,
separator,
repetition,
} => self.expand_repetition(
elements,
separator.as_ref(),
*repetition,
bindings,
syntax_ctx,
),
ExpansionElement::Delimited {
delimiter,
elements,
span,
} => {
let inner = self.expand_elements(elements, bindings, syntax_ctx)?;
Ok(vec![TokenTree::Delimited {
delimiter: *delimiter,
open_span: *span,
tokens: inner,
close_span: *span,
}])
}
}
}
fn expand_elements(
&mut self,
elements: &[ExpansionElement],
bindings: &Bindings,
syntax_ctx: SyntaxContext,
) -> MacroResult<Vec<TokenTree>> {
let mut result = Vec::new();
for element in elements {
result.extend(self.expand_element(element, bindings, syntax_ctx)?);
}
Ok(result)
}
fn expand_metavar(&mut self, name: &str, bindings: &Bindings) -> MacroResult<Vec<TokenTree>> {
let binding = bindings
.get(name)
.ok_or_else(|| MacroError::MetaVarNotFound {
name: name.to_string(),
})?;
match binding {
Binding::Single(value) => match value {
BindingValue::TokenTree(tt) => Ok(vec![tt.clone()]),
BindingValue::TokenTrees(tts) => Ok(tts.clone()),
},
Binding::Repeated(_) => {
Err(MacroError::RepetitionMismatch {
name: name.to_string(),
other: "non-repeated context".to_string(),
})
}
}
}
fn expand_repetition(
&mut self,
elements: &[ExpansionElement],
separator: Option<&TokenKind>,
_repetition: RepetitionKind,
bindings: &Bindings,
syntax_ctx: SyntaxContext,
) -> MacroResult<Vec<TokenTree>> {
let count = self.find_repetition_count(elements, bindings)?;
let mut result = Vec::new();
for i in 0..count {
if i > 0 {
if let Some(sep) = separator {
let sep_token = Token {
kind: sep.clone(),
span: Span::dummy(),
};
result.push(TokenTree::Token(sep_token));
}
}
let iter_bindings = self.extract_iteration(bindings, i);
result.extend(self.expand_elements(elements, &iter_bindings, syntax_ctx)?);
}
Ok(result)
}
fn find_repetition_count(
&self,
elements: &[ExpansionElement],
bindings: &Bindings,
) -> MacroResult<usize> {
for element in elements {
if let Some(count) = self.element_repetition_count(element, bindings) {
return Ok(count);
}
}
Ok(0)
}
fn element_repetition_count(
&self,
element: &ExpansionElement,
bindings: &Bindings,
) -> Option<usize> {
match element {
ExpansionElement::MetaVar(name) => {
if let Some(binding) = bindings.get(name) {
match binding {
Binding::Repeated(v) => Some(v.len()),
_ => None,
}
} else {
None
}
}
ExpansionElement::Delimited { elements, .. } => {
for e in elements {
if let Some(count) = self.element_repetition_count(e, bindings) {
return Some(count);
}
}
None
}
ExpansionElement::Repetition { elements, .. } => {
for e in elements {
if let Some(count) = self.element_repetition_count(e, bindings) {
return Some(count);
}
}
None
}
_ => None,
}
}
fn extract_iteration(&self, bindings: &Bindings, index: usize) -> Bindings {
let mut result = Bindings::new();
for (name, binding) in &bindings.bindings {
let iter_binding = match binding {
Binding::Single(v) => Binding::Single(v.clone()),
Binding::Repeated(v) => {
if index < v.len() {
v[index].clone()
} else {
Binding::Single(BindingValue::TokenTrees(Vec::new()))
}
}
};
result.insert(name.clone(), iter_binding);
}
result
}
}
pub fn expand_macro(
ctx: &MacroContext,
name: &str,
input: &[Token],
span: Span,
) -> MacroResult<Vec<TokenTree>> {
let mut expander = MacroExpander::new(ctx);
expander.expand(name, input, span)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::lexer::{Lexer, SourceFile};
fn lex(source: &str) -> Vec<Token> {
let file = SourceFile::anonymous(source);
let mut lexer = Lexer::new(&file);
lexer.tokenize().unwrap()
}
#[test]
fn test_expand_simple() {
let mut ctx = MacroContext::new();
use super::super::{
MacroDef, MacroId, MacroPattern, MacroRule, MetaVarKind, PatternElement,
};
let rule = MacroRule {
pattern: MacroPattern {
elements: vec![PatternElement::MetaVar {
name: "e".into(),
kind: MetaVarKind::TokenTree,
}],
},
expansion: MacroExpansion {
elements: vec![ExpansionElement::MetaVar("e".into())],
},
span: Span::dummy(),
};
let def = MacroDef {
id: MacroId::fresh(),
name: "identity".into(),
rules: vec![rule],
is_exported: false,
span: Span::dummy(),
};
ctx.register_macro(def);
let input = lex("42");
let result = expand_macro(&ctx, "identity", &input, Span::dummy()).unwrap();
assert_eq!(result.len(), 1); }
}