mlirformat_macros/lib.rs
1//! `#[mlir::op]` and `#[mlir::dialect]`. Neither writes conversion code: `op` spells each
2//! field's MLIR role in its serde name (decoded by `mlirformat::Field`) and forwards the
3//! conversions to the runtime; `dialect` turns an enum of ops into a registrable op table.
4//!
5//! ```ignore
6//! #[mlir::op("arith.maxsi", format = "$lhs `,` $rhs attr-dict `:` type($result)", results(result))]
7//! pub struct ArithMax<'a> {
8//! #[slot(result)] pub lhs: Value<'a>, // operand; its type is type variable `result`
9//! #[slot(result)] pub rhs: Value<'a>,
10//! #[dtype] pub result: Type<'a>, // type variable (result #0 by `results(...)`)
11//! }
12//! ```
13//!
14//! Field markers (a `Vec` field is variadic; MLIR names are the lowerCamelCase field names
15//! unless `name = "..."`):
16//!
17//! ```text
18//! #[slot(T)] operand(s) typed by type variable T
19//! #[dtype] #[dtype(i1)] #[dtype(i1 = x)] #[dtype(of = attr)] #[dtype(eq = x)] #[dtype(is = index)] #[dtype(el = x)]
20//! type variable; how custom form infers it when unspelled
21//! #[attr] #[attr(int = "i32")] #[attr(dialect = "arith.overflow", default = "<none>")]
22//! property (`bool`: unit attribute; enum: case value typed
23//! `int`, default i64); a dialect attribute is
24//! spelled without its prefix, its default is omitted
25//! #[region] #[successor]
26//! ```
27//!
28//! `attrs` (discardable attributes) and `loc` fields are added. Options: `format = "..."`,
29//! `results(a, b)`, `custom` (no `Op` impl: `print` / `parse` written by hand), `generic` (no custom
30//! form: always printed generic).
31//!
32//! `#[derive(mlir::Cases)]` on an integer enum: `TryFrom<i64>` by case value (the variant's
33//! position; hidden `__N` gap variants take none).
34//!
35//! `#[mlir::dialect("ns", ops = [A, B], ..)]` on a unit struct: the dialect, its op structs and
36//! its traits (`dependents = [..]`, `materializer`, `canonicalizer`); `params = f` structures
37//! `!ns.x<...>` / `#ns.x<...>` bodies (`Dialect::parse_params`).
38
39use proc_macro::TokenStream;
40use syn::{Error, ItemEnum, ItemStruct, parse_macro_input};
41
42mod cases;
43mod dialect;
44mod op;
45mod syntax;
46
47#[proc_macro_attribute]
48pub fn op(args: TokenStream, item: TokenStream) -> TokenStream {
49 let args = parse_macro_input!(args as op::OpArgs);
50 let item = parse_macro_input!(item as ItemStruct);
51 args.expand(item).unwrap_or_else(Error::into_compile_error).into()
52}
53
54#[proc_macro_attribute]
55pub fn dialect(args: TokenStream, item: TokenStream) -> TokenStream {
56 let args = parse_macro_input!(args as dialect::DialectArgs);
57 let item = parse_macro_input!(item as ItemStruct);
58 args.expand(item).unwrap_or_else(Error::into_compile_error).into()
59}
60
61#[proc_macro_derive(Cases)]
62pub fn cases(item: TokenStream) -> TokenStream {
63 let item = parse_macro_input!(item as ItemEnum);
64 cases::expand(&item).unwrap_or_else(Error::into_compile_error).into()
65}
66
67/// `syntax!`: the ODS `custom<X>` directives, as rules the format interpreter reads and writes
68/// from. Crate internal: `mlirformat::syntax` is its only caller.
69#[proc_macro]
70pub fn syntax(input: TokenStream) -> TokenStream {
71 let rules = parse_macro_input!(input as syntax::Rules);
72 rules.expand().unwrap_or_else(Error::into_compile_error).into()
73}