diff options
| author | Gustav Sörnäs <gustav@sornas.net> | 2021-01-30 12:10:49 +0100 |
|---|---|---|
| committer | Gustav Sörnäs <gustav@sornas.net> | 2021-01-30 12:10:49 +0100 |
| commit | e93e9e7ff4a73bc98a0dcd410eff87dffff3aa1c (patch) | |
| tree | 992024e5df64d7735618d2b0d0f85bbc313c6416 /tihdy_derive/src | |
| parent | 674695d7e28f03218aa5a3facd933b87d508ea7d (diff) | |
| download | sylt-e93e9e7ff4a73bc98a0dcd410eff87dffff3aa1c.tar.gz | |
external functions proc macro
Diffstat (limited to 'tihdy_derive/src')
| -rw-r--r-- | tihdy_derive/src/lib.rs | 86 |
1 files changed, 86 insertions, 0 deletions
diff --git a/tihdy_derive/src/lib.rs b/tihdy_derive/src/lib.rs new file mode 100644 index 0000000..cc28d64 --- /dev/null +++ b/tihdy_derive/src/lib.rs @@ -0,0 +1,86 @@ +use proc_macro::TokenStream; +use quote::quote; +use syn::{Expr, Pat, Token, parse::{Parse, ParseStream, Result}, parse_macro_input, punctuated::Punctuated}; + +struct ExternBlock { + pattern: Pat, + _arrow: Token![->], + return_ty: Expr, + _fat_arrow: Token![=>], + block: Expr, + _comma: Token![,], +} + +struct ExternFunction { + function: syn::Ident, + blocks: Vec<ExternBlock> +} + +impl Parse for ExternBlock { + fn parse(input: ParseStream) -> Result<Self> { + Ok(Self { + pattern: input.parse()?, + _arrow: input.parse()?, + return_ty: input.parse()?, + _fat_arrow: input.parse()?, + block: input.parse()?, + _comma: input.parse()?, + }) + } +} + +impl Parse for ExternFunction { + fn parse(input: ParseStream) -> Result<Self> { + let mut res = Self { + function: input.parse()?, + blocks: Vec::new(), + }; + while !input.is_empty() { + res.blocks.push(input.parse()?); + } + Ok(res) + } +} + +#[proc_macro] +pub fn extern_function(tokens: TokenStream) -> TokenStream { + let parsed: ExternFunction = parse_macro_input!(tokens); + let function = parsed.function; + + let typecheck_blocks: Vec<_> = parsed.blocks.iter().map(|block| { + let pat = block.pattern.clone(); + let ty = block.return_ty.clone(); + quote! { + #pat => { Ok(#ty.as_value()) } + } + }).collect(); + + let eval_blocks: Vec<_> = parsed.blocks.iter().map(|block| { + let pat = block.pattern.clone(); + let expr = block.block.clone(); + quote! { + #pat => #expr + } + }).collect(); + + let tokens = quote! { + pub fn #function ( + __values: &[tihdy::vm::Value], + __typecheck: bool + ) -> ::std::result::Result<tihdy::vm::Value, tihdy::error::ErrorKind> + { + if __typecheck { + match __values { + #(#typecheck_blocks),* + _ => Err(tihdy::error::ErrorKind::ExternTypeMismatch(stringify!(#function).to_string(), __values.iter().map(|v| tihdy::vm::Type::from(v)).collect())) + } + } else { + match __values { + #(#eval_blocks),* + _ => Err(tihdy::error::ErrorKind::ExternTypeMismatch(stringify!(#function).to_string(), __values.iter().map(|v| tihdy::vm::Type::from(v)).collect())) + } + } + } + }; + TokenStream::from(tokens) +} |
