]> git.proxmox.com Git - perlmod.git/commitdiff
macro: some refactoring
authorWolfgang Bumiller <w.bumiller@proxmox.com>
Fri, 10 Jan 2020 09:18:42 +0000 (10:18 +0100)
committerWolfgang Bumiller <w.bumiller@proxmox.com>
Fri, 10 Jan 2020 09:18:42 +0000 (10:18 +0100)
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
perlmod-macro/src/function.rs [new file with mode: 0644]
perlmod-macro/src/lib.rs
perlmod-macro/src/module.rs [new file with mode: 0644]
perlmod-macro/src/package.rs [new file with mode: 0644]

diff --git a/perlmod-macro/src/function.rs b/perlmod-macro/src/function.rs
new file mode 100644 (file)
index 0000000..14fe693
--- /dev/null
@@ -0,0 +1,149 @@
+use failure::Error;
+
+use proc_macro2::{Ident, TokenStream};
+
+use quote::quote;
+
+use crate::attribs::FunctionAttrs;
+
+pub struct XSub {
+    pub rust_name: Ident,
+    pub xs_name: Ident,
+    pub tokens: TokenStream,
+}
+
+pub fn handle_function(attr: FunctionAttrs, func: syn::ItemFn) -> Result<XSub, Error> {
+    //let vis = core::mem::replace(&mut func.vis, syn::Visibility::Inherited);
+    //if let syn::Visibility::Public(_) = vis {
+    //    // ok
+    //} else {
+    //    bail!(func.sig.fn_token => "only public functions can be exported as xsubs");
+    //}
+
+    let sig = &func.sig;
+    if !sig.generics.params.is_empty() {
+        bail!(&sig.generics => "generic functions cannot be exported as xsubs");
+    }
+
+    if sig.asyncness.is_some() {
+        bail!(&sig.asyncness => "async fns cannot be exported as xsubs");
+    }
+
+    let name = &sig.ident;
+    let xs_name = attr
+        .xs_name
+        .unwrap_or_else(|| Ident::new(&format!("xs_{}", name), name.span()));
+    let impl_xs_name = Ident::new(&format!("impl_xs_{}", name), name.span());
+
+    let mut extract_arguments = TokenStream::new();
+    let mut deserialized_arguments = TokenStream::new();
+    let mut passed_arguments = TokenStream::new();
+    for arg in &sig.inputs {
+        let pat_ty = match arg {
+            syn::FnArg::Receiver(_) => bail!(arg => "cannot export self-taking methods as xsubs"),
+            syn::FnArg::Typed(pt) => pt,
+        };
+
+        let arg_name = match &*pat_ty.pat {
+            syn::Pat::Ident(ident) => {
+                if ident.by_ref.is_some() {
+                    bail!(ident => "xsub does not support by-ref parameters");
+                }
+                if ident.subpat.is_some() {
+                    bail!(ident => "xsub does not support sub-patterns on parameters");
+                }
+                &ident.ident
+            }
+            _ => bail!(&pat_ty.pat => "xsub does not support this kind of parameter"),
+        };
+
+        let arg_type = &*pat_ty.ty;
+
+        let extracted_name = Ident::new(&format!("extracted_arg_{}", arg_name), arg_name.span());
+        let deserialized_name =
+            Ident::new(&format!("deserialized_arg_{}", arg_name), arg_name.span());
+
+        let missing_message = syn::LitStr::new("missing required parameter: '{}'", arg_name.span());
+
+        extract_arguments.extend(quote! {
+            let #extracted_name: ::perlmod::Value = match args.next() {
+                Some(arg) => ::perlmod::Value::from(arg),
+                None => {
+                    return Err(::perlmod::Value::new_string(#missing_message)
+                        .into_mortal()
+                        .into_raw());
+                }
+            };
+        });
+
+        deserialized_arguments.extend(quote! {
+            let #deserialized_name: #arg_type = match ::perlmod::from_value(#extracted_name) {
+                Ok(data) => data,
+                Err(err) => {
+                    return Err(::perlmod::Value::new_string(&err.to_string())
+                        .into_mortal()
+                        .into_raw());
+                }
+            };
+        });
+
+        if passed_arguments.is_empty() {
+            passed_arguments.extend(quote! { #deserialized_name });
+        } else {
+            passed_arguments.extend(quote! {, #deserialized_name });
+        }
+    }
+
+    let tokens = quote! {
+        #func
+
+        #[no_mangle]
+        pub extern "C" fn #xs_name(cv: &::perlmod::ffi::CV) {
+            unsafe {
+                match #impl_xs_name(cv) {
+                    Ok(sv) => ::perlmod::ffi::stack_push_raw(sv),
+                    Err(sv) => ::perlmod::ffi::croak(sv),
+                }
+            }
+        }
+
+        fn #impl_xs_name(
+            _cv: &::perlmod::ffi::CV,
+        ) -> Result<*mut ::perlmod::ffi::SV, *mut ::perlmod::ffi::SV> {
+            let argmark = unsafe { ::perlmod::ffi::pop_arg_mark() };
+            let mut args = argmark.iter();
+
+            #extract_arguments
+
+            drop(args);
+
+            #deserialized_arguments
+
+            unsafe {
+                argmark.set_stack();
+            }
+
+            let result = match #name(#passed_arguments) {
+                Ok(output) => output,
+                Err(err) => {
+                    return Err(::perlmod::Value::new_string(&err.to_string())
+                        .into_mortal()
+                        .into_raw());
+                }
+            };
+
+            match ::perlmod::to_value(&result) {
+                Ok(value) => Ok(value.into_mortal().into_raw()),
+                Err(err) => Err(::perlmod::Value::new_string(&err.to_string())
+                    .into_mortal()
+                    .into_raw()),
+            }
+        }
+    };
+
+    Ok(XSub {
+        rust_name: name.to_owned(),
+        xs_name,
+        tokens,
+    })
+}
index 43d4929c6a07887bef0a9bcfe2694cfe20c912f2..63bc616e3342ef2fac9d2d1e1381bf0b13059438 100644 (file)
@@ -2,18 +2,14 @@ extern crate proc_macro;
 extern crate proc_macro2;
 
 use std::convert::TryFrom;
-use std::iter::IntoIterator;
 
 use failure::Error;
 
 use proc_macro::TokenStream as TokenStream_1;
-use proc_macro2::{Ident, TokenStream};
+use proc_macro2::TokenStream;
 
-use quote::quote;
-use syn::parse::Parser;
 use syn::parse_macro_input;
-use syn::punctuated::Punctuated;
-use syn::{AttributeArgs, Token};
+use syn::AttributeArgs;
 
 macro_rules! format_err {
     ($span:expr => $($msg:tt)*) => { syn::Error::new_spanned($span, format!($($msg)*)) };
@@ -26,6 +22,9 @@ macro_rules! bail {
 }
 
 mod attribs;
+mod function;
+mod module;
+mod package;
 
 fn handle_error(mut item: TokenStream, data: Result<TokenStream, Error>) -> TokenStream {
     match data {
@@ -40,12 +39,6 @@ fn handle_error(mut item: TokenStream, data: Result<TokenStream, Error>) -> Toke
     }
 }
 
-struct XSub {
-    rust_name: Ident,
-    xs_name: Ident,
-    tokens: TokenStream,
-}
-
 /// Macro for starting a perl "package".
 #[proc_macro_attribute]
 pub fn package(attr: TokenStream_1, item: TokenStream_1) -> TokenStream_1 {
@@ -62,12 +55,19 @@ pub fn export(attr: TokenStream_1, item: TokenStream_1) -> TokenStream_1 {
     handle_error(item.clone(), export_impl(attr, item)).into()
 }
 
+// /// Proc macro to create a perl package file for rust functions.
+// #[proc_macro]
+// pub fn make_package(item: TokenStream_1) -> TokenStream_1 {
+//     let item: TokenStream = item.into();
+//     handle_error(item.clone(), make_package_impl(attr, item)).into()
+// }
+
 fn perlmod_impl(attr: AttributeArgs, item: TokenStream) -> Result<TokenStream, Error> {
     let item: syn::Item = syn::parse2(item)?;
 
     match item {
         syn::Item::Fn(func) => bail!(func => "did you mean to use the 'export' macro?"),
-        syn::Item::Mod(module) => handle_module(attr, module),
+        syn::Item::Mod(module) => module::handle_module(attr, module),
         _ => bail!(item => "expected module or function"),
     }
 }
@@ -76,299 +76,6 @@ fn export_impl(attr: AttributeArgs, item: TokenStream) -> Result<TokenStream, Er
     let func: syn::ItemFn = syn::parse2(item)?;
 
     let attr = attribs::FunctionAttrs::try_from(attr)?;
-    let func = handle_function(attr, func)?;
+    let func = function::handle_function(attr, func)?;
     Ok(func.tokens)
 }
-
-fn handle_function(attr: attribs::FunctionAttrs, func: syn::ItemFn) -> Result<XSub, Error> {
-    //let vis = core::mem::replace(&mut func.vis, syn::Visibility::Inherited);
-    //if let syn::Visibility::Public(_) = vis {
-    //    // ok
-    //} else {
-    //    bail!(func.sig.fn_token => "only public functions can be exported as xsubs");
-    //}
-
-    let sig = &func.sig;
-    if !sig.generics.params.is_empty() {
-        bail!(&sig.generics => "generic functions cannot be exported as xsubs");
-    }
-
-    if sig.asyncness.is_some() {
-        bail!(&sig.asyncness => "async fns cannot be exported as xsubs");
-    }
-
-    let name = &sig.ident;
-    let xs_name = attr
-        .xs_name
-        .unwrap_or_else(|| Ident::new(&format!("xs_{}", name), name.span()));
-    let impl_xs_name = Ident::new(&format!("impl_xs_{}", name), name.span());
-
-    let mut extract_arguments = TokenStream::new();
-    let mut deserialized_arguments = TokenStream::new();
-    let mut passed_arguments = TokenStream::new();
-    for arg in &sig.inputs {
-        let pat_ty = match arg {
-            syn::FnArg::Receiver(_) => bail!(arg => "cannot export self-taking methods as xsubs"),
-            syn::FnArg::Typed(pt) => pt,
-        };
-
-        let arg_name = match &*pat_ty.pat {
-            syn::Pat::Ident(ident) => {
-                if ident.by_ref.is_some() {
-                    bail!(ident => "xsub does not support by-ref parameters");
-                }
-                if ident.subpat.is_some() {
-                    bail!(ident => "xsub does not support sub-patterns on parameters");
-                }
-                &ident.ident
-            }
-            _ => bail!(&pat_ty.pat => "xsub does not support this kind of parameter"),
-        };
-
-        let arg_type = &*pat_ty.ty;
-
-        let extracted_name = Ident::new(&format!("extracted_arg_{}", arg_name), arg_name.span());
-        let deserialized_name =
-            Ident::new(&format!("deserialized_arg_{}", arg_name), arg_name.span());
-
-        let missing_message = syn::LitStr::new("missing required parameter: '{}'", arg_name.span());
-
-        extract_arguments.extend(quote! {
-            let #extracted_name: ::perlmod::Value = match args.next() {
-                Some(arg) => ::perlmod::Value::from(arg),
-                None => {
-                    return Err(::perlmod::Value::new_string(#missing_message)
-                        .into_mortal()
-                        .into_raw());
-                }
-            };
-        });
-
-        deserialized_arguments.extend(quote! {
-            let #deserialized_name: #arg_type = match ::perlmod::from_value(#extracted_name) {
-                Ok(data) => data,
-                Err(err) => {
-                    return Err(::perlmod::Value::new_string(&err.to_string())
-                        .into_mortal()
-                        .into_raw());
-                }
-            };
-        });
-
-        if passed_arguments.is_empty() {
-            passed_arguments.extend(quote! { #deserialized_name });
-        } else {
-            passed_arguments.extend(quote! {, #deserialized_name });
-        }
-    }
-
-    let tokens = quote! {
-        #func
-
-        #[no_mangle]
-        pub extern "C" fn #xs_name(cv: &::perlmod::ffi::CV) {
-            unsafe {
-                match #impl_xs_name(cv) {
-                    Ok(sv) => ::perlmod::ffi::stack_push_raw(sv),
-                    Err(sv) => ::perlmod::ffi::croak(sv),
-                }
-            }
-        }
-
-        fn #impl_xs_name(
-            _cv: &::perlmod::ffi::CV,
-        ) -> Result<*mut ::perlmod::ffi::SV, *mut ::perlmod::ffi::SV> {
-            let argmark = unsafe { ::perlmod::ffi::pop_arg_mark() };
-            let mut args = argmark.iter();
-
-            #extract_arguments
-
-            drop(args);
-
-            #deserialized_arguments
-
-            unsafe {
-                argmark.set_stack();
-            }
-
-            let result = match #name(#passed_arguments) {
-                Ok(output) => output,
-                Err(err) => {
-                    return Err(::perlmod::Value::new_string(&err.to_string())
-                        .into_mortal()
-                        .into_raw());
-                }
-            };
-
-            match ::perlmod::to_value(&result) {
-                Ok(value) => Ok(value.into_mortal().into_raw()),
-                Err(err) => Err(::perlmod::Value::new_string(&err.to_string())
-                    .into_mortal()
-                    .into_raw()),
-            }
-        }
-    };
-
-    Ok(XSub {
-        rust_name: name.to_owned(),
-        xs_name,
-        tokens,
-    })
-}
-
-const LIB_NAME_DEFAULT: &str = r#"($pkg =~ /(?:^|::)([^:]+)$/)"#;
-
-const MODULE_HEAD: &str = r#"
-use strict;
-use warnings;
-use DynaLoader ();
-
-my $LIB;
-
-sub __load_shared_lib {
-    return if $LIB;
-
-    my ($pkg) = @_;
-
-    my $auto_path = ($pkg =~ s!::!/!gr);
-    my ($mod_name) = {{LIB_NAME}};
-
-    my @dirs = (map "-L$_/auto/$auto_path", @INC);
-    my (@mod_files) = DynaLoader::dl_findfile(@dirs, '-L./target/debug', $mod_name);
-    die "failed to locate shared library for '$pkg' (lib${mod_name}.so)\n" if !@mod_files;
-
-    $LIB = DynaLoader::dl_load_file($mod_files[0])
-        or die "failed to load library '$mod_files[0]'\n";
-}
-
-sub newXS {
-    my ($perl_func_name, $full_symbol_name, $filename) = @_;
-
-    my $sym  = DynaLoader::dl_find_symbol($LIB, $full_symbol_name);
-    die "failed to locate '$full_symbol_name'\n" if !defined $sym;
-    DynaLoader::dl_install_xsub($perl_func_name, $sym, $filename);
-}
-
-BEGIN {
-    __load_shared_lib(__PACKAGE__);
-"#;
-
-const MODULE_TAIL: &str = "}\n";
-
-struct Export {
-    rust_name: Ident,
-    xs_name: Ident,
-    file_name: String,
-}
-
-struct Package {
-    attrs: attribs::ModuleAttrs,
-    exported: Vec<Export>,
-}
-
-impl Package {
-    fn with_attrs(attr: AttributeArgs) -> Result<Self, Error> {
-        Ok(Self {
-            attrs: attribs::ModuleAttrs::try_from(attr)?,
-            exported: Vec::new(),
-        })
-    }
-
-    fn export_named(&mut self, rust_name: Ident, xs_name: Ident, file_name: String) {
-        self.exported.push(Export {
-            rust_name,
-            xs_name,
-            file_name,
-        });
-    }
-
-    fn export_direct(&mut self, name: Ident, file_name: String) {
-        let xs_name = Ident::new(&format!("xs_{}", name), name.span());
-        self.exported.push(Export {
-            rust_name: name,
-            xs_name,
-            file_name,
-        });
-    }
-
-    fn write(&self) -> Result<(), Error> {
-        let mut source = format!("package {};\n{}", self.attrs.package_name, MODULE_HEAD);
-
-        for export in &self.exported {
-            source = format!(
-                "{}    newXS('{}', '{}', \"{}\");\n",
-                source,
-                export.rust_name,
-                export.xs_name,
-                export.file_name.replace('"', "\\\""),
-            );
-        }
-
-        source.push_str(MODULE_TAIL);
-
-        if let Some(lib) = &self.attrs.lib_name {
-            source = source.replace("{{LIB_NAME}}", &format!("('{}')", lib));
-        } else {
-            source = source.replace("{{LIB_NAME}}", LIB_NAME_DEFAULT);
-        }
-
-        let path = std::path::Path::new(&self.attrs.file_name);
-        if let Some(parent) = path.parent() {
-            std::fs::create_dir_all(parent)?;
-        }
-        std::fs::write(path, source.as_bytes())?;
-
-        Ok(())
-    }
-}
-
-fn handle_module(attr: AttributeArgs, mut module: syn::ItemMod) -> Result<TokenStream, Error> {
-    let mut package = Package::with_attrs(attr)?;
-
-    if let Some((_brace, ref mut items)) = module.content {
-        for item in items.iter_mut() {
-            match core::mem::replace(item, syn::Item::Verbatim(TokenStream::new())) {
-                syn::Item::Fn(mut func) => {
-                    let mut attribs = None;
-                    for attr in std::mem::replace(&mut func.attrs, Default::default()) {
-                        if attr.path.is_ident("export") {
-                            if attribs.is_some() {
-                                bail!(attr => "multiple 'export' attributes not allowed");
-                            }
-
-                            let args: AttributeArgs =
-                                Punctuated::<syn::NestedMeta, Token![,]>::parse_terminated
-                                    .parse2(attr.tokens)?
-                                    .into_iter()
-                                    .collect();
-
-                            attribs = Some(attribs::FunctionAttrs::try_from(args)?);
-                        } else {
-                            // retain the attribute
-                            func.attrs.push(attr);
-                        }
-                    }
-
-                    // if we removed an #[export] macro this is an exported function:
-                    if let Some(attribs) = attribs {
-                        let func = handle_function(attribs, func)?;
-                        *item = syn::Item::Verbatim(func.tokens);
-
-                        package.export_named(
-                            func.rust_name,
-                            func.xs_name,
-                            "src/FIXME.rs".to_string(),
-                        );
-                    } else {
-                        *item = syn::Item::Fn(func);
-                    }
-                }
-                other => *item = other,
-            }
-        }
-    }
-
-    package.write()?;
-
-    Ok(quote! { #module })
-}
diff --git a/perlmod-macro/src/module.rs b/perlmod-macro/src/module.rs
new file mode 100644 (file)
index 0000000..eeb8ec2
--- /dev/null
@@ -0,0 +1,65 @@
+use std::convert::TryFrom;
+use std::iter::IntoIterator;
+
+use failure::Error;
+
+use proc_macro2::TokenStream;
+
+use quote::quote;
+use syn::parse::Parser;
+use syn::punctuated::Punctuated;
+use syn::{AttributeArgs, Token};
+
+use crate::attribs::FunctionAttrs;
+use crate::package::Package;
+
+pub fn handle_module(attr: AttributeArgs, mut module: syn::ItemMod) -> Result<TokenStream, Error> {
+    let mut package = Package::with_attrs(attr)?;
+
+    if let Some((_brace, ref mut items)) = module.content {
+        for item in items.iter_mut() {
+            match core::mem::replace(item, syn::Item::Verbatim(TokenStream::new())) {
+                syn::Item::Fn(mut func) => {
+                    let mut attribs = None;
+                    for attr in std::mem::replace(&mut func.attrs, Default::default()) {
+                        if attr.path.is_ident("export") {
+                            if attribs.is_some() {
+                                bail!(attr => "multiple 'export' attributes not allowed");
+                            }
+
+                            let args: AttributeArgs =
+                                Punctuated::<syn::NestedMeta, Token![,]>::parse_terminated
+                                    .parse2(attr.tokens)?
+                                    .into_iter()
+                                    .collect();
+
+                            attribs = Some(FunctionAttrs::try_from(args)?);
+                        } else {
+                            // retain the attribute
+                            func.attrs.push(attr);
+                        }
+                    }
+
+                    // if we removed an #[export] macro this is an exported function:
+                    if let Some(attribs) = attribs {
+                        let func = crate::function::handle_function(attribs, func)?;
+                        *item = syn::Item::Verbatim(func.tokens);
+
+                        package.export_named(
+                            func.rust_name,
+                            func.xs_name,
+                            "src/FIXME.rs".to_string(),
+                        );
+                    } else {
+                        *item = syn::Item::Fn(func);
+                    }
+                }
+                other => *item = other,
+            }
+        }
+    }
+
+    package.write()?;
+
+    Ok(quote! { #module })
+}
diff --git a/perlmod-macro/src/package.rs b/perlmod-macro/src/package.rs
new file mode 100644 (file)
index 0000000..4f41f40
--- /dev/null
@@ -0,0 +1,115 @@
+use std::convert::TryFrom;
+
+use failure::Error;
+
+use proc_macro2::Ident;
+
+use syn::AttributeArgs;
+
+use crate::attribs::ModuleAttrs;
+
+const LIB_NAME_DEFAULT: &str = r#"($pkg =~ /(?:^|::)([^:]+)$/)"#;
+
+const MODULE_HEAD: &str = r#"
+use strict;
+use warnings;
+use DynaLoader ();
+
+my $LIB;
+
+sub __load_shared_lib {
+    return if $LIB;
+
+    my ($pkg) = @_;
+
+    my $auto_path = ($pkg =~ s!::!/!gr);
+    my ($mod_name) = {{LIB_NAME}};
+
+    my @dirs = (map "-L$_/auto/$auto_path", @INC);
+    my (@mod_files) = DynaLoader::dl_findfile(@dirs, '-L./target/debug', $mod_name);
+    die "failed to locate shared library for '$pkg' (lib${mod_name}.so)\n" if !@mod_files;
+
+    $LIB = DynaLoader::dl_load_file($mod_files[0])
+        or die "failed to load library '$mod_files[0]'\n";
+}
+
+sub newXS {
+    my ($perl_func_name, $full_symbol_name, $filename) = @_;
+
+    my $sym  = DynaLoader::dl_find_symbol($LIB, $full_symbol_name);
+    die "failed to locate '$full_symbol_name'\n" if !defined $sym;
+    DynaLoader::dl_install_xsub($perl_func_name, $sym, $filename);
+}
+
+BEGIN {
+    __load_shared_lib(__PACKAGE__);
+"#;
+
+const MODULE_TAIL: &str = "}\n";
+
+struct Export {
+    rust_name: Ident,
+    xs_name: Ident,
+    file_name: String,
+}
+
+pub struct Package {
+    attrs: ModuleAttrs,
+    exported: Vec<Export>,
+}
+
+impl Package {
+    pub fn with_attrs(attr: AttributeArgs) -> Result<Self, Error> {
+        Ok(Self {
+            attrs: ModuleAttrs::try_from(attr)?,
+            exported: Vec::new(),
+        })
+    }
+
+    pub fn export_named(&mut self, rust_name: Ident, xs_name: Ident, file_name: String) {
+        self.exported.push(Export {
+            rust_name,
+            xs_name,
+            file_name,
+        });
+    }
+
+    pub fn export_direct(&mut self, name: Ident, file_name: String) {
+        let xs_name = Ident::new(&format!("xs_{}", name), name.span());
+        self.exported.push(Export {
+            rust_name: name,
+            xs_name,
+            file_name,
+        });
+    }
+
+    pub fn write(&self) -> Result<(), Error> {
+        let mut source = format!("package {};\n{}", self.attrs.package_name, MODULE_HEAD);
+
+        for export in &self.exported {
+            source = format!(
+                "{}    newXS('{}', '{}', \"{}\");\n",
+                source,
+                export.rust_name,
+                export.xs_name,
+                export.file_name.replace('"', "\\\""),
+            );
+        }
+
+        source.push_str(MODULE_TAIL);
+
+        if let Some(lib) = &self.attrs.lib_name {
+            source = source.replace("{{LIB_NAME}}", &format!("('{}')", lib));
+        } else {
+            source = source.replace("{{LIB_NAME}}", LIB_NAME_DEFAULT);
+        }
+
+        let path = std::path::Path::new(&self.attrs.file_name);
+        if let Some(parent) = path.parent() {
+            std::fs::create_dir_all(parent)?;
+        }
+        std::fs::write(path, source.as_bytes())?;
+
+        Ok(())
+    }
+}