rune/macros/
macro_compiler.rs

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
//! Macro compiler.

use crate::alloc::prelude::*;
use crate::ast;
use crate::ast::Spanned;
use crate::compile::{self, ErrorKind, ItemMeta};
use crate::indexing::Indexer;
use crate::macros::{MacroContext, ToTokens};
use crate::parse::{Parse, Parser};

use super::TokenStream;

pub(crate) struct MacroCompiler<'a, 'b, 'arena> {
    pub(crate) item_meta: ItemMeta,
    pub(crate) idx: &'a mut Indexer<'b, 'arena>,
}

impl MacroCompiler<'_, '_, '_> {
    /// Compile the given macro into the given output type.
    pub(crate) fn eval_macro<T>(&mut self, macro_call: &ast::MacroCall) -> compile::Result<T>
    where
        T: Parse,
    {
        let span = macro_call.span();

        if !self.idx.q.options.macros {
            return Err(compile::Error::msg(
                span,
                "macros must be enabled with `-O macros=true`",
            ));
        }

        let named = self.idx.q.convert_path(&macro_call.path)?;
        let hash = self.idx.q.pool.item_type_hash(named.item);

        let Some(handler) = self.idx.q.context.lookup_macro(hash) else {
            return Err(compile::Error::new(
                span,
                ErrorKind::MissingMacro {
                    item: self.idx.q.pool.item(named.item).try_to_owned()?,
                },
            ));
        };

        let input_stream = &macro_call.input;

        let token_stream = {
            let mut macro_context = MacroContext {
                macro_span: span,
                input_span: macro_call.input_span(),
                item_meta: self.item_meta,
                idx: self.idx,
            };

            handler(&mut macro_context, input_stream)?
        };

        let mut parser = Parser::from_token_stream(&token_stream, span);
        let output = parser.parse::<T>()?;
        parser.eof()?;

        Ok(output)
    }

    /// Compile the given macro into the given output type.
    pub(crate) fn eval_attribute_macro<T>(
        &mut self,
        attribute: &ast::Attribute,
        item: &ast::Item,
    ) -> compile::Result<Option<T>>
    where
        T: Parse,
    {
        let span = attribute.span();

        if !self.idx.q.options.macros {
            return Ok(None);
        }

        let named = self.idx.q.convert_path(&attribute.path)?;

        let hash = self.idx.q.pool.item_type_hash(named.item);

        let handler = match self.idx.q.context.lookup_attribute_macro(hash) {
            Some(handler) => handler,
            None => {
                return Ok(None);
            }
        };

        let input_stream = &attribute.input;

        let token_stream = {
            let mut macro_context = MacroContext {
                macro_span: attribute.span(),
                input_span: attribute.input_span(),
                item_meta: self.item_meta,
                idx: self.idx,
            };

            let mut item_stream = TokenStream::new();
            item.to_tokens(&mut macro_context, &mut item_stream)?;

            handler(&mut macro_context, input_stream, &item_stream)?
        };

        let mut parser = Parser::from_token_stream(&token_stream, span);

        parser.parse_all().map(Some)
    }
}