|
@@ -0,0 +1,245 @@
|
|
|
+mod yaml_def;
|
|
|
+use proc_macro::TokenStream;
|
|
|
+use quote::{quote};
|
|
|
+use syn::{LitStr, Type};
|
|
|
+use proc_macro_error::{proc_macro_error, abort_call_site};
|
|
|
+use crate::yaml_def::{Function};
|
|
|
+
|
|
|
+extern crate syn;
|
|
|
+extern crate std;
|
|
|
+
|
|
|
+#[proc_macro]
|
|
|
+#[proc_macro_error]
|
|
|
+pub fn generate_wasi_import(input: TokenStream) -> TokenStream{
|
|
|
+ let ast = syn::parse_macro_input!(input as LitStr);
|
|
|
+ let yaml_path = ast.value();
|
|
|
+ let yaml_content = match std::fs::read_to_string(&yaml_path) {
|
|
|
+ Ok(content) => content,
|
|
|
+ Err(err) => {
|
|
|
+ abort_call_site! {
|
|
|
+ "Failed to read YAML file: {}", err;
|
|
|
+ note = "check YAML file path: {}", yaml_path;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ };
|
|
|
+ let yaml_root: yaml_def::YamlRoot = match serde_yaml::from_str(&yaml_content) {
|
|
|
+ Ok(root) => root,
|
|
|
+ Err(err) => {
|
|
|
+ abort_call_site! {
|
|
|
+ "Failed to parse YAML file: {}", err;
|
|
|
+ note = "check YAML file syntax";
|
|
|
+ }
|
|
|
+ }
|
|
|
+ };
|
|
|
+ let modules = yaml_root.iter().map(|i| {
|
|
|
+ let mod_name = i.0;
|
|
|
+ let mod_val = i.1;
|
|
|
+ let fns_result = mod_val.iter().map(|j| {
|
|
|
+ let fn_name = j.0;
|
|
|
+ let fn_val = j.1;
|
|
|
+ let fn_efid = &fn_val.efid;
|
|
|
+ let fn_efid_ident = syn::Ident::new(fn_efid, proc_macro2::Span::call_site());
|
|
|
+ let fn_params = process_fn_signature_param(fn_val, mod_name, fn_name);
|
|
|
+ let fn_results = process_fn_signature_results(fn_val, mod_name, fn_name);
|
|
|
+ let fn_retval = match fn_results {
|
|
|
+ Some(s) => {
|
|
|
+ quote! {
|
|
|
+ -> #s
|
|
|
+ }
|
|
|
+ },
|
|
|
+ None => {
|
|
|
+ proc_macro2::TokenStream::new()
|
|
|
+ }
|
|
|
+ };
|
|
|
+ quote! {
|
|
|
+ #[doc = #fn_name ]
|
|
|
+ fn #fn_efid_ident(#fn_params) #fn_retval;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ quote! {
|
|
|
+ #[link(wasm_import_module = #mod_name)]
|
|
|
+ extern "C" {
|
|
|
+ #(#fns_result)*
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+
|
|
|
+ let module_wraps = yaml_root.iter().map(|i| {
|
|
|
+ let mod_name = i.0;
|
|
|
+ let mod_name_ident = syn::Ident::new(mod_name, proc_macro2::Span::call_site());
|
|
|
+ let mod_val = i.1;
|
|
|
+ let fns_result = mod_val.iter().map(|j| {
|
|
|
+ let fn_name = j.0;
|
|
|
+ let fn_val = j.1;
|
|
|
+ let fn_efid = &fn_val.efid;
|
|
|
+ let fn_name_ident = syn::Ident::new(fn_name, proc_macro2::Span::call_site());
|
|
|
+ let fn_efid_ident = syn::Ident::new(fn_efid, proc_macro2::Span::call_site());
|
|
|
+ let fn_params = process_fn_signature_param(fn_val, mod_name, fn_name);
|
|
|
+ let fn_call_params = process_fn_call_param(fn_val);
|
|
|
+ let fn_results = process_fn_signature_results(fn_val, mod_name, fn_name);
|
|
|
+ let fn_retval = match fn_results {
|
|
|
+ Some(s) => {
|
|
|
+ quote! {
|
|
|
+ -> #s
|
|
|
+ }
|
|
|
+ },
|
|
|
+ None => {
|
|
|
+ proc_macro2::TokenStream::new()
|
|
|
+ }
|
|
|
+ };
|
|
|
+ quote! {
|
|
|
+ pub unsafe fn #fn_name_ident(#fn_params) #fn_retval {
|
|
|
+ crate::raw_fn::#fn_efid_ident(#fn_call_params)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+ quote! {
|
|
|
+ pub mod #mod_name_ident {
|
|
|
+ #(#fns_result)*
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ let result = quote! {
|
|
|
+ #(#modules)*
|
|
|
+
|
|
|
+ #(#module_wraps)*
|
|
|
+ };
|
|
|
+
|
|
|
+ result.into()
|
|
|
+}
|
|
|
+
|
|
|
+fn process_fn_signature_param(input: &Function, mod_name: &str, fn_name: &str) -> proc_macro2::TokenStream {
|
|
|
+ match &input.params {
|
|
|
+ Some(s) => {
|
|
|
+ if s.len() == 0 {
|
|
|
+ return proc_macro2::TokenStream::new();
|
|
|
+ }
|
|
|
+ let params_iter = s.iter().map(|i| {
|
|
|
+ let param_name = &i.name;
|
|
|
+ let param_name_ident = syn::Ident::new(¶m_name, proc_macro2::Span::call_site());
|
|
|
+ let is_use_xtyp;
|
|
|
+ let param_type = match &i.xtyp {
|
|
|
+ Some(s) => {
|
|
|
+ if s.as_str() == "enum" {
|
|
|
+ is_use_xtyp = false;
|
|
|
+ &i.rtype
|
|
|
+ }else{
|
|
|
+ is_use_xtyp = true;
|
|
|
+ s
|
|
|
+ }
|
|
|
+ },
|
|
|
+ None => {
|
|
|
+ is_use_xtyp = false;
|
|
|
+ &i.rtype
|
|
|
+ }
|
|
|
+ };
|
|
|
+ let param_type_parse_res = syn::parse_str::<Type>(param_type.as_str());
|
|
|
+ let param_type_ast = match param_type_parse_res {
|
|
|
+ Ok(ast) => ast,
|
|
|
+ Err(err) => {
|
|
|
+ let fn_fullname = format!("{}::{}", mod_name, fn_name);
|
|
|
+ let p_src = if is_use_xtyp {"xtyp"}else{"type"};
|
|
|
+ let yaml_field_path = format!("{}.{}.{}.{}", mod_name, fn_name, param_name, p_src);
|
|
|
+ abort_call_site! {
|
|
|
+ "Failed to read param {} of {}: '{}': {}", p_src, fn_fullname, param_type, err;
|
|
|
+ note = "Syntax error of this yaml field: {}", yaml_field_path;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ };
|
|
|
+ quote!(#param_name_ident: #param_type_ast)
|
|
|
+ });
|
|
|
+ quote! {
|
|
|
+ #(#params_iter),*
|
|
|
+ }
|
|
|
+ },
|
|
|
+ None => {
|
|
|
+ proc_macro2::TokenStream::new()
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+fn process_fn_call_param(input: &Function) -> proc_macro2::TokenStream {
|
|
|
+ match &input.params {
|
|
|
+ Some(s) => {
|
|
|
+ if s.len() == 0 {
|
|
|
+ return proc_macro2::TokenStream::new();
|
|
|
+ }
|
|
|
+ let params_iter = s.iter().map(|i| {
|
|
|
+ let param_name = &i.name;
|
|
|
+ syn::Ident::new(¶m_name, proc_macro2::Span::call_site())
|
|
|
+ });
|
|
|
+ quote! {
|
|
|
+ #(#params_iter),*
|
|
|
+ }
|
|
|
+ },
|
|
|
+ None => {
|
|
|
+ proc_macro2::TokenStream::new()
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+fn process_fn_signature_results(input: &Function, mod_name: &str, fn_name: &str) -> Option<proc_macro2::TokenStream> {
|
|
|
+ match &input.results {
|
|
|
+ Some(s) => {
|
|
|
+ if s.len() == 0 {
|
|
|
+ return None;
|
|
|
+ }
|
|
|
+ let mut results_iter = s.iter().map(|i| {
|
|
|
+ let is_use_xtyp;
|
|
|
+ let result_type = match &i.xtyp {
|
|
|
+ Some(s) => {
|
|
|
+ if s.as_str() == "enum" {
|
|
|
+ is_use_xtyp = false;
|
|
|
+ &i.rtype
|
|
|
+ }else{
|
|
|
+ is_use_xtyp = true;
|
|
|
+ s
|
|
|
+ }
|
|
|
+ },
|
|
|
+ None => {
|
|
|
+ is_use_xtyp = false;
|
|
|
+ &i.rtype
|
|
|
+ }
|
|
|
+ };
|
|
|
+ let result_type_parse_res = syn::parse_str::<Type>(result_type.as_str());
|
|
|
+ let result_type_ast = match result_type_parse_res {
|
|
|
+ Ok(ast) => ast,
|
|
|
+ Err(err) => {
|
|
|
+ let fn_fullname = format!("{}::{}", mod_name, fn_name);
|
|
|
+ let p_src = if is_use_xtyp {"xtyp"}else{"type"};
|
|
|
+ let yaml_field_path = format!("{}.{}.{}.{}", mod_name, fn_name, &i.name, p_src);
|
|
|
+ abort_call_site! {
|
|
|
+ "Failed to read result {} of {}: '{}': {}", p_src, fn_fullname, result_type, err;
|
|
|
+ note = "Syntax error of this yaml field: {}", yaml_field_path;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ };
|
|
|
+ result_type_ast
|
|
|
+ });
|
|
|
+ Some(if results_iter.len() == 1 {
|
|
|
+ let result_res = results_iter.next();
|
|
|
+ let result = match result_res {
|
|
|
+ Some(s) => s,
|
|
|
+ None => {
|
|
|
+ abort_call_site! {
|
|
|
+ "iter error in {}::{}: there should be one result in iter but got none.", mod_name, fn_name;
|
|
|
+ note = "This is macro bug. Please report it.";
|
|
|
+ }
|
|
|
+ }
|
|
|
+ };
|
|
|
+ quote! {
|
|
|
+ #result
|
|
|
+ }
|
|
|
+ }else{
|
|
|
+ quote! {
|
|
|
+ (#(#results_iter),*)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ },
|
|
|
+ None => {
|
|
|
+ None
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|