1use cfg_elif::expr::feature;
4use core::error::Error;
5use proc_macro::TokenStream;
6use proc_macro2::Literal;
7use quote::{ToTokens, quote};
8use stak_compiler::CompileError;
9use stak_macro_util::{convert_result, read_source_file};
10use std::path::{MAIN_SEPARATOR_STR, Path};
11use syn::{Ident, LitStr, Token, parse::Parse, parse_macro_input};
12
13struct IncludeModuleInput {
14 path: LitStr,
15 module: Option<Ident>,
16}
17
18impl Parse for IncludeModuleInput {
19 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
20 let path = input.parse()?;
21 let mut module = None;
22
23 if input.parse::<Option<Token![,]>>()?.is_some() {
24 if let Some(value) = input.parse()? {
25 input.parse::<Option<Token![,]>>()?;
26 module = Some(value);
27 }
28 };
29
30 Ok(Self { path, module })
31 }
32}
33
34#[proc_macro]
39pub fn include_module(input: TokenStream) -> TokenStream {
40 let input = parse_macro_input!(input as IncludeModuleInput);
41
42 convert_result(include_result(&input)).into()
43}
44
45fn include_result(input: &IncludeModuleInput) -> Result<proc_macro2::TokenStream, Box<dyn Error>> {
46 let path = format!("{}", Path::new("src").join(input.path.value()).display());
47 let full_path = quote!(concat!(env!("OUT_DIR"), #MAIN_SEPARATOR_STR, #path));
48 let module = input
49 .module
50 .as_ref()
51 .map_or_else(|| quote!(stak::module), |module| module.to_token_stream());
52
53 Ok(feature!(if ("hot-reload") {
54 quote! {
55 {
56 static MODULE: #module::HotReloadModule = #module::HotReloadModule::new(#full_path);
57 #module::UniversalModule::HotReload(&MODULE)
58 }
59 }
60 } else {
61 quote!(#module::UniversalModule::Static(#module::StaticModule::new(include_bytes!(#full_path))))
62 }))
63}
64
65#[proc_macro]
73pub fn compile_r7rs(input: TokenStream) -> TokenStream {
74 let input = parse_macro_input!(input as LitStr);
75
76 convert_result(generate_r7rs(&input.value())).into()
77}
78
79#[proc_macro]
87pub fn include_r7rs(input: TokenStream) -> TokenStream {
88 let input = parse_macro_input!(input as LitStr);
89
90 convert_result((|| {
91 let source = generate_r7rs(&read_source_file(input.clone())?)?;
92
93 Ok(quote!({
94 static _SOURCE: &str = include_str!(#input);
96 #source
97 }))
98 })())
99 .into()
100}
101
102fn generate_r7rs(source: &str) -> Result<proc_macro2::TokenStream, Box<dyn Error>> {
103 generate_scheme(source, |source, target| {
104 stak_compiler::compile_r7rs(source, target)
105 })
106}
107
108#[proc_macro]
116pub fn compile_bare(input: TokenStream) -> TokenStream {
117 let input = parse_macro_input!(input as LitStr);
118
119 convert_result(generate_bare(&input.value())).into()
120}
121
122#[proc_macro]
130pub fn include_bare(input: TokenStream) -> TokenStream {
131 let input = parse_macro_input!(input as LitStr);
132
133 convert_result((|| generate_bare(&read_source_file(input)?))()).into()
134}
135
136fn generate_bare(source: &str) -> Result<proc_macro2::TokenStream, Box<dyn Error>> {
137 generate_scheme(source, |source, target| {
138 stak_compiler::compile_bare(source, target)
139 })
140}
141
142fn generate_scheme(
143 source: &str,
144 compile: fn(&[u8], &mut Vec<u8>) -> Result<(), CompileError>,
145) -> Result<proc_macro2::TokenStream, Box<dyn Error>> {
146 let mut target = vec![];
147
148 compile(source.as_bytes(), &mut target)?;
149
150 let target = Literal::byte_string(&target);
151
152 Ok(quote! { #target })
153}