diff --git a/Cargo.lock b/Cargo.lock index 767d3caf2..155620756 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -101,6 +101,7 @@ version = "0.1.33-nightly.1" dependencies = [ "erg_common", "erg_compiler", + "erg_proc_macros", "lsp-types", "molc", "serde", @@ -143,6 +144,15 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "erg_proc_macros" +version = "0.6.21-nightly.1" +dependencies = [ + "erg_common", + "quote", + "syn 1.0.109", +] + [[package]] name = "form_urlencoded" version = "1.2.0" @@ -247,9 +257,9 @@ dependencies = [ [[package]] name = "molc" -version = "0.1.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a19b669aab31ca7552fc43cb9ab08e325113aa090f7bf97a2112b3d6241ba898" +checksum = "9485212d67a88d2169ee683420e1a2fe763afb96a3901ebfabb5ba095d0d2eaa" dependencies = [ "lsp-types", "serde", @@ -375,7 +385,7 @@ checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.31", ] [[package]] @@ -397,7 +407,7 @@ checksum = "8725e1dfadb3a50f7e5ce0b1a540466f6ed3fe7a0fca2ac2b8b831d31316bd00" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.31", ] [[package]] @@ -436,6 +446,17 @@ version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "syn" version = "2.0.31" diff --git a/Cargo.toml b/Cargo.toml index 8a3c1973e..bccff3fb6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,7 @@ members = [ "crates/erg_compiler", "crates/erg_parser", "crates/els", + "crates/erg_proc_macros/", ] [workspace.package] @@ -68,6 +69,7 @@ erg_common = { version = "0.6.21-nightly.1", path = "./crates/erg_common" } erg_parser = { version = "0.6.21-nightly.1", path = "./crates/erg_parser" } erg_compiler = { version = "0.6.21-nightly.1", path = "./crates/erg_compiler" } els = { version = "0.1.33-nightly.1", path = "./crates/els" } +erg_proc_macros = { version = "0.6.21-nightly.1", path = "./crates/erg_proc_macros" } [dependencies] erg_common = { workspace = true } diff --git a/crates/els/Cargo.toml b/crates/els/Cargo.toml index 48f2c4a2e..76f9ebab6 100644 --- a/crates/els/Cargo.toml +++ b/crates/els/Cargo.toml @@ -23,11 +23,14 @@ experimental = ["erg_common/experimental", "erg_compiler/experimental"] [dependencies] erg_common = { workspace = true, features = ["els"] } erg_compiler = { workspace = true, features = ["els"] } -molc = { version = "0.1.0" } +molc = { version = "0.2" } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0.85" lsp-types = { version = "0.93.2", features = ["proposed"] } +[dev-dependencies] +erg_proc_macros = { workspace = true } + [lib] path = "lib.rs" diff --git a/crates/els/tests/test.rs b/crates/els/tests/test.rs index 6d39e657b..f77321bd2 100644 --- a/crates/els/tests/test.rs +++ b/crates/els/tests/test.rs @@ -9,6 +9,7 @@ const FILE_B: &str = "tests/b.er"; const FILE_IMPORTS: &str = "tests/imports.er"; use els::{NormalizedUrl, Server}; +use erg_proc_macros::exec_new_thread; use molc::{add_char, oneline_range}; #[test] @@ -146,6 +147,7 @@ fn test_goto_definition() -> Result<(), Box> { } #[test] +#[exec_new_thread] fn test_folding_range() -> Result<(), Box> { let mut client = Server::bind_fake_client(); client.request_initialize()?; diff --git a/crates/erg_proc_macros/Cargo.toml b/crates/erg_proc_macros/Cargo.toml new file mode 100644 index 000000000..aa3940fb1 --- /dev/null +++ b/crates/erg_proc_macros/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "erg_proc_macros" +version.workspace = true +authors.workspace = true +license.workspace = true +edition.workspace = true +repository.workspace = true +homepage.workspace = true + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +erg_common = { workspace = true } +syn = { version = "1.0", features = ["full"] } +quote = "1.0" + +[lib] +proc-macro = true diff --git a/crates/erg_proc_macros/src/lib.rs b/crates/erg_proc_macros/src/lib.rs new file mode 100644 index 000000000..6b0cfca89 --- /dev/null +++ b/crates/erg_proc_macros/src/lib.rs @@ -0,0 +1,56 @@ +use proc_macro::TokenStream; + +use quote::quote; +use syn::{PathArguments, ReturnType, Type, TypePath}; + +/// ```ignore +/// #[exec_new_thread] +/// fn foo() -> Result> { +/// ... +/// } +/// ``` +/// ↓ ↓ +/// ```ignore +/// fn foo() -> Result> { +/// fn error(msg: impl Into) -> std::io::Error { +/// std::io::Error::new(std::io::ErrorKind::Other, msg.into()) +/// } +/// fn f() -> Result<(), Box> { +/// {...}.map_err(|e| Box::new(error(e.to_string())) as _) +/// } +/// exec_new_thread(f, "foo").map_err(|e| e as _) +/// } +/// ``` +#[proc_macro_attribute] +pub fn exec_new_thread(_attr: TokenStream, item: TokenStream) -> TokenStream { + let mut item_fn = syn::parse_macro_input!(item as syn::ItemFn); + let name = item_fn.sig.ident.to_string(); + let ReturnType::Type(_, out) = &item_fn.sig.output else { + todo!() + }; + let Type::Path(TypePath { path, .. }) = out.as_ref() else { + todo!() + }; + let result_t = path.segments.first().unwrap(); + let PathArguments::AngleBracketed(args) = &result_t.arguments else { + todo!() + }; + let t = args.args.first().unwrap(); + let name = syn::LitStr::new(&name, item_fn.sig.ident.span()); + let block = item_fn.block; + let block = syn::parse_quote! {{ + fn error(msg: impl Into) -> std::io::Error { + std::io::Error::new(std::io::ErrorKind::Other, msg.into()) + } + fn _f() -> Result<#t, Box> { + #block + } + fn f() -> Result<#t, Box> { + _f().map_err(|e| Box::new(error(e.to_string())) as _) + } + erg_common::spawn::exec_new_thread(f, #name).map_err(|e| e as _) + }}; + item_fn.block = Box::new(block); + let item = quote! { #item_fn }; + item.into() +}