Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 0fd4edf

Browse files
Merge pull request #2 from Tsukuba-Programming-Lab/#1_SetupProject
#1 基礎を整えた
2 parents d7696a8 + e531218 commit 0fd4edf

File tree

15 files changed

+403
-1
lines changed

15 files changed

+403
-1
lines changed

‎Cargo.lock

Lines changed: 87 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎Cargo.toml

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,26 @@
11
[package]
22
name = "mymodifer"
3-
version = "0.1.0"
43
edition = "2024"
4+
version.workspace = true
55

66
[dependencies]
7+
# TODO
8+
9+
[workspace]
10+
resovler = "2"
11+
members = [
12+
# crates
13+
"crates/core",
14+
"crates/macros",
15+
16+
# examples
17+
"examples/simple",
18+
]
19+
exclude = []
20+
21+
[workspace.package]
22+
version = "0.1.0"
23+
24+
[workspace.dependencies]
25+
anyhow = "1.0.96"
26+
thiserror = "2.0.11"

‎README.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# My Modifier
2+
3+
```rust
4+
#[modifier]
5+
trait Dev {}
6+
7+
#[modifier_callee]
8+
fn for_developper() {
9+
// ...
10+
}
11+
12+
fn main() {
13+
// Ok
14+
dev! {{
15+
for_developper();
16+
}}
17+
18+
// Compile error
19+
for_developper();
20+
}
21+
```
22+
23+
## Examples
24+
25+
- [simple](examples/simple)
26+
27+
```
28+
$ cargo run -p example_simple
29+
Hello, World!
30+
```

‎crates/core/Cargo.toml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
[package]
2+
name = "mymodifier_core"
3+
edition = "2024"
4+
version.workspace = true
5+
6+
[dependencies]
7+
anyhow = { workspace = true }
8+
thiserror = { workspace = true }
9+
mymodifier_macros = { path = "../macros" }

‎crates/core/src/lib.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
pub use mymodifier_macros::{
2+
modifier,
3+
modifier_caller,
4+
modifier_callee,
5+
};

‎crates/macros/Cargo.toml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
[package]
2+
name = "mymodifier_macros"
3+
edition = "2024"
4+
version.workspace = true
5+
6+
[dependencies]
7+
proc-macro2 = "1.0"
8+
quote = "1.0"
9+
syn = { version = "2.0", features = ["full", "extra-traits"] }
10+
11+
[lib]
12+
proc-macro = true

‎crates/macros/src/impl.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
pub mod modifier;
2+
pub mod modifier_caller;
3+
pub mod modifier_callee;

‎crates/macros/src/impl/modifier.rs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
use proc_macro2::{TokenStream, Ident};
2+
use quote::quote;
3+
use syn::ItemTrait;
4+
5+
/// 対象: トレイト
6+
/// 動作: 文脈導入用の宣言型マクロを生成
7+
pub fn proc_macro_impl(_args: TokenStream, ast: ItemTrait) -> TokenStream {
8+
let trait_name = &ast.ident;
9+
let macro_ident = as_macro_ident(trait_name);
10+
11+
quote! {
12+
// トレイト本体
13+
#ast
14+
15+
// 文脈マクロ
16+
#[macro_export]
17+
macro_rules! #macro_ident {
18+
({ $($body:tt)* }) => {
19+
// 文脈を用意
20+
struct Ctx;
21+
22+
// 文脈付き呼び出しへの変換 (関数定義ではなくブロックに変換される)
23+
#[modifier_caller(#trait_name)]
24+
fn __mymodifier_caller() {
25+
$($body)*
26+
}
27+
};
28+
29+
($($body:tt)*) => {{
30+
// 文脈付き呼び出しへの変換 (関数定義ではなくブロックに変換される)
31+
#[modifier_caller(#trait_name)]
32+
fn __mymodifier_caller() {
33+
$($body)*
34+
}
35+
}};
36+
}
37+
}
38+
}
39+
40+
fn as_macro_ident(ident: &Ident) -> TokenStream {
41+
let mut result = String::new();
42+
for c in ident.to_string().chars() {
43+
if c.is_uppercase() {
44+
result.push('_');
45+
result.push(c.to_ascii_lowercase());
46+
} else {
47+
result.push(c);
48+
}
49+
}
50+
51+
let result = if result.starts_with('_') {
52+
&result[1..]
53+
} else {
54+
&result
55+
};
56+
57+
result.parse().unwrap()
58+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
use proc_macro2::TokenStream;
2+
use quote::quote;
3+
use syn::{FnArg, ItemFn};
4+
5+
/// 対象: 関数
6+
/// 動作: 文脈への制約を追加
7+
pub fn proc_macro_impl(args: TokenStream, ast: ItemFn) -> TokenStream {
8+
let target_trait = args;
9+
10+
let fn_visibility = ast.vis;
11+
let fn_ident = ast.sig.ident;
12+
let fn_ret_type = ast.sig.output;
13+
let fn_body = ast.block;
14+
15+
let fn_args = ast.sig.inputs;
16+
let fn_args = if let Some(FnArg::Receiver(..)) = fn_args.first() {
17+
let receiver = &fn_args[0];
18+
let remains = fn_args.iter().skip(1).collect::<Vec<_>>();
19+
quote! { #receiver, ctx: Ctx, #(#remains),*}
20+
} else {
21+
quote! { ctx: Ctx, #fn_args }
22+
};
23+
24+
quote! {
25+
// マクロ適用関数
26+
#fn_visibility fn #fn_ident <Ctx> (#fn_args) #fn_ret_type
27+
where
28+
// 制約
29+
Ctx: #target_trait,
30+
{
31+
// 関数本体
32+
#fn_body
33+
}
34+
}
35+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
use proc_macro2::TokenStream;
2+
use quote::{quote, ToTokens};
3+
use syn::{Expr, ItemFn, Stmt};
4+
5+
/// 対象: 関数
6+
/// 動作: 文脈付き呼び出しを行うための前準備を生成 & 文脈付き呼び出しへの変換
7+
pub fn proc_macro_impl(args: TokenStream, ast: ItemFn) -> TokenStream {
8+
let target_trait = args;
9+
10+
let stmts = &ast
11+
.block
12+
.stmts
13+
.iter()
14+
.map(append_ctx_if_calling)
15+
.collect::<Vec<_>>();
16+
17+
quote! {
18+
// 前準備
19+
#[allow(non_local_definitions)]
20+
impl #target_trait for Ctx {}
21+
22+
// 関数本体
23+
#(#stmts);*
24+
}
25+
}
26+
27+
fn append_ctx_if_calling(stmt: &Stmt) -> TokenStream {
28+
let append_ctx = |expr: &Expr| {
29+
if let Expr::Call(call) = expr {
30+
let func = call.func.to_token_stream();
31+
let args = call.args.to_token_stream();
32+
quote! { #func (Ctx, #args) }
33+
} else if let Expr::MethodCall(call) = expr {
34+
let receiver = call.receiver.to_token_stream();
35+
let method = call.method.to_token_stream();
36+
let args = call.args.to_token_stream();
37+
quote! { #receiver . #method (Ctx, #args) }
38+
} else {
39+
stmt.to_token_stream()
40+
}
41+
};
42+
43+
match stmt {
44+
Stmt::Expr(expr, _) => append_ctx(expr),
45+
_ => stmt.to_token_stream(),
46+
}
47+
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /