1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
use cast::{caster_impl, register_caster_impl};
use interfaces::{control_flow, data_flow, region_interface};
use proc_macro::TokenStream;

mod cast;
mod format;
mod interfaces;
mod op;
mod ty;

/// Implement an [Op](orzir_core::Op) for the given struct.
///
/// This will first generate a `new` constructor for the struct, which returns
/// an `ArenaPtr<OpObj>` object.
///
/// The `#[mnemonic = "..."]` attribute is used to specify the mnemonic of the
/// operation. The mnemonic string will be split by the first dot character. The
/// first part will be used as the dialect mnemonic, and the second part will be
/// used as the operation mnemonic.
///
/// The `#[interfaces(...)]` and `#[verifiers(...)]` attributes are used to
/// implement/register the interfaces and verifiers for the operation.
///
/// By specifying the verifiers, the corresponding traits will be implemented
/// automatically. and the trait casters will be registered in the context when
/// calling [`Op::register`](orzir_core::Op::register). Also, the
/// [`VerifyInterfaces`](orzir_core::VerifyInterfaces) trait will be implemented
/// for the operation, by calling all the `verify` function in the verifier
/// traits.
///
/// The registering process of interfaces will also be generated by specifying
/// the `interfaces` attribute. However, they will not be implemented
/// automatically.
///
/// The verifiers can only be implemented when deriving the `Op` trait. But the
/// interfaces can be implemented in a plug-in manner. This can be done by
/// calling the `register_caster` macro in the `register` function of any
/// dialect.
///
/// To make a struct valid for the `Op` derive, the `#[metadata]` attribute must
/// be specified for the metadata field of the struct, which contains the
/// `self_ptr` and `parent_block` fields.
///
/// For an [`Op`](orzir_core::Op) trait to be valid, the following traits must
/// be implemented:
/// - [`Print`](orzir_core::Print)
/// - [`Parse`](orzir_core::Parse)
/// - [`Verify`](orzir_core::Verify)
/// - [`DataFlow`](orzir_core::DataFlow)
/// - [`ControlFlow`](orzir_core::ControlFlow)
/// - [`RegionInterface`](orzir_core::RegionInterface)
///
/// They can be derived or implemented manually.
#[proc_macro_derive(Op, attributes(mnemonic, verifiers, interfaces, metadata))]
pub fn op(item: TokenStream) -> TokenStream {
    let ast = syn::parse_macro_input!(item as syn::DeriveInput);
    op::derive_impl(&ast)
        .unwrap_or_else(|err| err.to_compile_error())
        .into()
}

/// Implement a [Ty](orzir_core::Ty) for the given struct.
///
/// This is similar to the [`Op`] derive, but for the `Ty` trait, except that
/// the constructor will be `get` for singleton style construction.
#[proc_macro_derive(Ty, attributes(mnemonic, verifiers, interfaces))]
pub fn ty(item: TokenStream) -> TokenStream {
    let ast = syn::parse_macro_input!(item as syn::DeriveInput);
    ty::derive_impl(&ast)
        .unwrap_or_else(|err| err.to_compile_error())
        .into()
}

/// Create a caster for casting from one trait object to another.
///
/// # Example
///
/// Below is an example of getting the [`Caster`](orzir_core::Caster) for
/// casting from `ModuleOp` to `IsIsolatedFromAbove`.
///
/// ```rust,ignore
/// caster!(ModuleOp => IsIsolatedFromAbove)
/// ```
#[proc_macro]
pub fn caster(input: TokenStream) -> TokenStream {
    caster_impl(input.into())
        .unwrap_or_else(|err| err.to_compile_error())
        .into()
}

/// Register a trait object caster in the context.
///
/// This is used if the interface/trait is not defined in the same crate as the
/// operation/type.
///
/// # Example
///
/// Below is an example of registering the caster from `ModuleOp` to
/// `IsIsolatedFromAbove`.
///
/// ```rust,ignore
/// register_caster!(ctx, ModuleOp => IsIsolatedFromAbove)
/// ```
#[proc_macro]
pub fn register_caster(input: TokenStream) -> TokenStream {
    register_caster_impl(input.into()).unwrap().into()
}

/// Implement the [RegionInterface](orzir_core::RegionInterface) for the given
/// struct.
///
/// For the region interface, the `#[region]` attribute is used to specify the
/// region field of the struct.
///
/// There are currently two ways to specify these fields:
///
/// 1. Using the `#[region(n)]` where `n` is the index of the field.
/// 2. Using the `#[region(...)]`, which means the field is a vector of the
///    regions.
///
/// The type of the fields should be `ArenaPtr<Region>` or
/// `Vec<ArenaPtr<Region>>`.
#[proc_macro_derive(RegionInterface, attributes(region))]
pub fn derive_region_interface(item: TokenStream) -> TokenStream {
    let ast = syn::parse_macro_input!(item as syn::DeriveInput);
    region_interface::derive_impl(&ast)
        .unwrap_or_else(|err| err.to_compile_error())
        .into()
}

/// Implement the [ControlFlow](orzir_core::ControlFlow) for the given struct.
///
/// The `#[successor]` attribute is used to specify the successor field of the
/// struct.
///
/// There are currently two ways to specify these fields:
///
/// 1. Using the `#[successor(n)]` where `n` is the index of the field.
/// 2. Using the `#[successor(...)]`, which means the field is a vector of the
///    successors.
///
/// The type of the fields should be `Successor` or `Vec<Successor>`.
#[proc_macro_derive(ControlFlow, attributes(successor))]
pub fn derive_control_flow(item: TokenStream) -> TokenStream {
    let ast = syn::parse_macro_input!(item as syn::DeriveInput);
    control_flow::derive_impl(&ast)
        .unwrap_or_else(|err| err.to_compile_error())
        .into()
}

/// Implement the [DataFlow](orzir_core::DataFlow) for the given struct.
///
/// The `#[result]` and `#[operand]` attributes are used to specify the result
/// and operand fields of the struct.
///
/// There are currently two ways to specify these fields:
///
/// 1. Using the `#[result(n)]` or `#[operand(n)]` where `n` is the index of the
///    value.
/// 2. Using the `#[result(...)]` or `#[operand(...)]`, which means the field is
///    a vector of the values.
///
/// The type of the fields should be `ArenaPtr<Value>` or
/// `Vec<ArenaPtr<Value>>`.
#[proc_macro_derive(DataFlow, attributes(result, operand))]
pub fn derive_data_flow(item: TokenStream) -> TokenStream {
    let ast = syn::parse_macro_input!(item as syn::DeriveInput);
    data_flow::derive_impl(&ast)
        .unwrap_or_else(|err| err.to_compile_error())
        .into()
}

/// Derive the `Parse` trait for the given operation.
///
/// This support very simple grammar, for more complex grammar, the trait can be
/// implemented manually.
#[proc_macro_derive(Parse, attributes(format, repeat))]
pub fn derive_parse(item: TokenStream) -> TokenStream {
    let ast = syn::parse_macro_input!(item as syn::DeriveInput);
    format::derive_parse_impl(&ast)
        .unwrap_or_else(|err| err.to_compile_error())
        .into()
}

/// Derive the `Print` trait for the given operation.
///
/// This support very simple grammar, for more complex grammar, the trait can be
/// implemented manually.
#[proc_macro_derive(Print, attributes(format, repeat))]
pub fn derive_print(item: TokenStream) -> TokenStream {
    let ast = syn::parse_macro_input!(item as syn::DeriveInput);
    format::derive_print_impl(&ast)
        .unwrap_or_else(|err| err.to_compile_error())
        .into()
}

#[proc_macro_derive(Verify)]
pub fn derive_verify(item: TokenStream) -> TokenStream {
    let ast = syn::parse_macro_input!(item as syn::DeriveInput);
    let ident = ast.ident;

    let output = quote::quote! {
        impl ::orzir_core::Verify for #ident {}
    };

    output.into()
}