use proc_macro::TokenStream;
use proc_macro2::Literal;
use quote::quote;
use stak_compiler::CompileError;
use std::{env, error::Error, fs::read_to_string, path::Path};
use syn::{parse_macro_input, LitStr};
#[proc_macro]
pub fn compile_r7rs(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as LitStr);
convert_result(generate_r7rs(&input.value()))
}
#[proc_macro]
pub fn include_r7rs(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as LitStr);
convert_result((|| generate_r7rs(&read_file(input)?))())
}
fn generate_r7rs(source: &str) -> Result<TokenStream, Box<dyn Error>> {
generate_scheme(source, |source, target| {
stak_compiler::compile_r7rs(source, target)
})
}
#[proc_macro]
pub fn compile_bare(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as LitStr);
convert_result(generate_bare(&input.value()))
}
#[proc_macro]
pub fn include_bare(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as LitStr);
convert_result((|| generate_bare(&read_file(input)?))())
}
fn generate_bare(source: &str) -> Result<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<TokenStream, Box<dyn Error>> {
let mut target = vec![];
compile(source.as_bytes(), &mut target)?;
let target = Literal::byte_string(&target);
Ok(quote! { #target }.into())
}
fn read_file(path: LitStr) -> Result<String, Box<dyn Error>> {
Ok(read_to_string(
Path::new(&env::var("CARGO_MANIFEST_DIR")?)
.join("src")
.join(path.value()),
)?)
}
fn convert_result(result: Result<TokenStream, Box<dyn Error>>) -> TokenStream {
result.unwrap_or_else(|error| {
let message = error.to_string();
quote! { compile_error!(#message) }.into()
})
}