Files
arret
arret_compiler
arret_lsp_server
arret_rfi_derive
arret_runtime
arret_runtime_syntax
arret_syntax
stdlib
  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()),
    ))
}