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((|| generate_r7rs(&read_source_file(input)?))()).into()
91}
92
93fn generate_r7rs(source: &str) -> Result<proc_macro2::TokenStream, Box<dyn Error>> {
94 generate_scheme(source, |source, target| {
95 stak_compiler::compile_r7rs(source, target)
96 })
97}
98
99#[proc_macro]
107pub fn compile_bare(input: TokenStream) -> TokenStream {
108 let input = parse_macro_input!(input as LitStr);
109
110 convert_result(generate_bare(&input.value())).into()
111}
112
113#[proc_macro]
121pub fn include_bare(input: TokenStream) -> TokenStream {
122 let input = parse_macro_input!(input as LitStr);
123
124 convert_result((|| generate_bare(&read_source_file(input)?))()).into()
125}
126
127fn generate_bare(source: &str) -> Result<proc_macro2::TokenStream, Box<dyn Error>> {
128 generate_scheme(source, |source, target| {
129 stak_compiler::compile_bare(source, target)
130 })
131}
132
133fn generate_scheme(
134 source: &str,
135 compile: fn(&[u8], &mut Vec<u8>) -> Result<(), CompileError>,
136) -> Result<proc_macro2::TokenStream, Box<dyn Error>> {
137 let mut target = vec![];
138
139 compile(source.as_bytes(), &mut target)?;
140
141 let target = Literal::byte_string(&target);
142
143 Ok(quote! { #target })
144}