stak_macro/
lib.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
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
137
138
139
140
141
//! Macros to bundle and use Scheme programs.

use cfg_elif::expr::feature;
use core::error::Error;
use proc_macro::TokenStream;
use proc_macro2::Literal;
use quote::{quote, ToTokens};
use stak_compiler::CompileError;
use stak_macro_util::{convert_result, read_source_file};
use std::path::{Path, MAIN_SEPARATOR_STR};
use syn::{parse::Parse, parse_macro_input, Ident, LitStr, Token};

struct IncludeModuleInput {
    path: LitStr,
    module: Option<Ident>,
}

impl Parse for IncludeModuleInput {
    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
        let path = input.parse()?;
        let mut module = None;

        if input.parse::<Option<Token![,]>>()?.is_some() {
            if let Some(value) = input.parse()? {
                input.parse::<Option<Token![,]>>()?;
                module = Some(value);
            }
        };

        Ok(Self { path, module })
    }
}

/// Includes bytecodes of a R7RS Scheme module built by the
/// [`stak_build`](https://docs.rs/stak-build) crate.
///
/// See the [`stak`](https://docs.rs/stak) crate's documentation for full examples.
#[proc_macro]
pub fn include_module(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as IncludeModuleInput);

    convert_result(include_result(&input)).into()
}

fn include_result(input: &IncludeModuleInput) -> Result<proc_macro2::TokenStream, Box<dyn Error>> {
    let path = format!("{}", Path::new("src").join(input.path.value()).display());
    let full_path = quote!(concat!(env!("OUT_DIR"), #MAIN_SEPARATOR_STR, #path));
    let module = input
        .module
        .as_ref()
        .map_or_else(|| quote!(stak::module), |module| module.to_token_stream());

    Ok(feature!(if ("hot-reload") {
        quote!(#module::UniversalModule::from_hot_reload_path(#full_path))
    } else {
        quote!(#module::UniversalModule::from_bytecode(
            include_bytes!(#full_path)
        ))
    }))
}

/// Compiles a module in R7RS Scheme into bytecodes.
///
/// # Examples
///
/// ```rust
/// const BYTECODE: &[u8] = stak_macro::compile_r7rs!("(define x 42)");
/// ```
#[proc_macro]
pub fn compile_r7rs(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as LitStr);

    convert_result(generate_r7rs(&input.value())).into()
}

/// Includes a module in R7RS Scheme as bytecodes.
///
/// # Examples
///
/// ```rust
/// const BYTECODE: &[u8] = stak_macro::include_r7rs!("foo.scm");
/// ```
#[proc_macro]
pub fn include_r7rs(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as LitStr);

    convert_result((|| generate_r7rs(&read_source_file(input)?))()).into()
}

fn generate_r7rs(source: &str) -> Result<proc_macro2::TokenStream, Box<dyn Error>> {
    generate_scheme(source, |source, target| {
        stak_compiler::compile_r7rs(source, target)
    })
}

/// Compiles a module in Scheme into bytecodes with only built-ins.
///
/// # Examples
///
/// ```rust
/// const BYTECODE: &[u8] = stak_macro::compile_bare!("($$define x 42)");
/// ```
#[proc_macro]
pub fn compile_bare(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as LitStr);

    convert_result(generate_bare(&input.value())).into()
}

/// Includes a module in Scheme as bytecodes with only built-ins.
///
/// # Examples
///
/// ```rust
/// const BYTECODE: &[u8] = stak_macro::include_bare!("foo.scm");
/// ```
#[proc_macro]
pub fn include_bare(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as LitStr);

    convert_result((|| generate_bare(&read_source_file(input)?))()).into()
}

fn generate_bare(source: &str) -> Result<proc_macro2::TokenStream, Box<dyn Error>> {
    generate_scheme(source, |source, target| {
        stak_compiler::compile_bare(source, target)
    })
}

fn generate_scheme(
    source: &str,
    compile: fn(&[u8], &mut Vec<u8>) -> Result<(), CompileError>,
) -> Result<proc_macro2::TokenStream, Box<dyn Error>> {
    let mut target = vec![];

    compile(source.as_bytes(), &mut target)?;

    let target = Literal::byte_string(&target);

    Ok(quote! { #target })
}