1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
mod expander;
mod linker;
mod matcher;

use std::sync::Arc;

use arret_syntax::span::Span;

use crate::context::ModuleId;
use crate::hir::error::{Error, ErrorKind, Result};
use crate::hir::macros::expander::expand_rule;
use crate::hir::macros::linker::{link_rule_vars, VarLinks};
use crate::hir::macros::matcher::match_rule;
use crate::hir::ns::{Ident, NsDatum};
use crate::hir::scope::Scope;

#[derive(Debug)]
pub struct Rule {
    pattern_span: Span,
    pattern: Box<[NsDatum]>,
    template: NsDatum,
    var_links: VarLinks,
}

#[derive(Debug)]
pub struct Macro {
    rules: Box<[Rule]>,
}

impl Macro {
    pub fn new(rules: Box<[Rule]>) -> Arc<Self> {
        Arc::new(Self { rules })
    }
}

fn starts_with_zero_or_more(data: &[NsDatum]) -> bool {
    match data {
        [_, NsDatum::Ident(_, ident), ..] => ident.is_ellipsis(),
        _ => false,
    }
}

fn get_escaped_ident(data: &[NsDatum]) -> Option<&Ident> {
    match data {
        [NsDatum::Ident(_, ellipsis_ident), NsDatum::Ident(_, escaped_ident)]
            if ellipsis_ident.is_ellipsis() =>
        {
            Some(escaped_ident)
        }
        _ => None,
    }
}

fn lower_macro_rule_datum(
    scope: &Scope<'_>,
    self_ident: &Ident,
    rule_datum: NsDatum,
) -> Result<Rule> {
    let (span, mut rule_values) = if let NsDatum::Vector(span, vs) = rule_datum {
        (span, vs.into_vec())
    } else {
        return Err(Error::new(
            rule_datum.span(),
            ErrorKind::ExpectedMacroRuleVec(rule_datum.description()),
        ));
    };

    if rule_values.len() != 2 {
        return Err(Error::new(
            span,
            ErrorKind::WrongMacroRuleVecCount(rule_values.len()),
        ));
    }

    let template = rule_values.pop().unwrap();
    let pattern_datum = rule_values.pop().unwrap();

    let (pattern_span, pattern) = if let NsDatum::List(span, vs) = pattern_datum {
        (span, vs)
    } else {
        return Err(Error::new(
            pattern_datum.span(),
            ErrorKind::ExpectedMacroRulePatternList(pattern_datum.description()),
        ));
    };

    let var_links = link_rule_vars(scope, self_ident, pattern_span, &pattern, &template)?;

    Ok(Rule {
        pattern_span,
        pattern,
        template,
        var_links,
    })
}

pub fn lower_macro_rules(
    scope: &Scope<'_>,
    self_ident: &Ident,
    macro_rules_data: Vec<NsDatum>,
) -> Result<Arc<Macro>> {
    let rules = macro_rules_data
        .into_iter()
        .map(|rule_datum| lower_macro_rule_datum(scope, self_ident, rule_datum))
        .collect::<Result<Box<[Rule]>>>()?;

    Ok(Macro::new(rules))
}

pub fn expand_macro<'s, 'p>(
    scope: &'s mut Scope<'p>,
    invocation_span: Span,
    module_id: Option<ModuleId>,
    mac: &Arc<Macro>,
    arg_data: &[NsDatum],
) -> Result<NsDatum> {
    for rule in mac.rules.iter() {
        let match_result = match_rule(rule, arg_data);

        if let Ok(match_data) = match_result {
            return Ok(expand_rule(
                scope,
                module_id,
                mac,
                &match_data,
                &rule.var_links,
                &rule.template,
            ));
        }
    }

    Err(Error::new(
        invocation_span,
        ErrorKind::NoMacroRule(mac.rules.iter().map(|rule| rule.pattern_span).collect()),
    ))
}