use proc_macro::TokenStream;
use proc_macro2::TokenStream as TS;
use quote::{quote, ToTokens};
use syn::{parse_macro_input, Data, DeriveInput, Fields, Ident, Index, Type};
#[proc_macro_derive(Alga, attributes(alga))]
pub fn derive_alga(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
match expand(&input) {
Ok(ts) => ts.into(),
Err(e) => e.into_compile_error().into(),
}
}
#[derive(PartialEq, Clone, Copy)]
enum Level {
Monoid,
Group,
Ring,
Field,
}
impl Level {
fn parse(input: &DeriveInput) -> syn::Result<Self> {
let mut level = Level::Ring;
for attr in &input.attrs {
if attr.path().is_ident("alga") {
attr.parse_nested_meta(|meta| {
if meta.path.is_ident("level") {
let value = meta.value()?;
let s: syn::LitStr = value.parse()?;
level = match s.value().as_str() {
"Monoid" => Level::Monoid,
"Group" => Level::Group,
"Ring" => Level::Ring,
"Field" => Level::Field,
other => {
return Err(meta.error(format!(
"unknown alga level `{other}` (expected Monoid, Group, Ring or Field)"
)))
}
};
Ok(())
} else {
Err(meta.error("unknown `#[alga(...)]` option (only `level` is supported)"))
}
})?;
}
}
Ok(level)
}
}
struct FieldInfo {
ty: Type,
suffix: TS,
name: Option<Ident>,
}
fn expand(input: &DeriveInput) -> syn::Result<TS> {
let name = &input.ident;
let generics = &input.generics;
let (impl_g, ty_g, where_g) = generics.split_for_impl();
let fields = match &input.data {
Data::Struct(s) => fields_of(&s.fields)?,
Data::Enum(_) | Data::Union(_) => {
return Err(syn::Error::new_spanned(input, "#[derive(Alga)] supports structs only"))
}
};
let level = Level::parse(input)?;
let g = Gen { name, impl_g: &impl_g, ty_g: &ty_g, where_g };
let mut out = TS::new();
let add_max = if level == Level::Monoid { "Monoid" } else { "Group" };
out.extend(g.single_op_chain(&fields, "Additive", add_max));
out.extend(g.single_op_chain(&fields, "Multiplicative", "Monoid"));
if matches!(level, Level::Ring | Level::Field) {
out.extend(g.two_op_chain(&fields, level));
out.extend(g.module_level(&fields));
}
Ok(out)
}
fn fields_of(fields: &Fields) -> syn::Result<Vec<FieldInfo>> {
let mut out = vec![];
match fields {
Fields::Named(n) => {
for f in &n.named {
let id = f.ident.clone().unwrap();
out.push(FieldInfo { ty: f.ty.clone(), suffix: id.to_token_stream(), name: Some(id) });
}
}
Fields::Unnamed(u) => {
for (i, f) in u.unnamed.iter().enumerate() {
let idx = Index::from(i);
out.push(FieldInfo { ty: f.ty.clone(), suffix: idx.to_token_stream(), name: None });
}
}
Fields::Unit => {
return Err(syn::Error::new_spanned(fields, "#[derive(Alga)] needs at least one field"))
}
}
if out.is_empty() {
return Err(syn::Error::new_spanned(fields, "#[derive(Alga)] needs at least one field"));
}
Ok(out)
}
fn self_access(f: &FieldInfo) -> TS {
let s = &f.suffix;
quote!(self.#s)
}
fn rhs_access(f: &FieldInfo) -> TS {
let s = &f.suffix;
quote!(rhs.#s)
}
fn construct(fields: &[FieldInfo], exprs: &[TS]) -> TS {
match fields.first().map(|f| f.name.is_some()) {
Some(true) => {
let pairs = fields.iter().zip(exprs).map(|(f, e)| {
let n = f.name.as_ref().unwrap();
quote!(#n: #e)
});
quote!(Self { #(#pairs),* })
}
_ => quote!(Self( #(#exprs),* )),
}
}
fn field_types(fields: &[FieldInfo]) -> Vec<&Type> {
let mut seen: Vec<String> = vec![];
fields
.iter()
.map(|f| &f.ty)
.filter(|t| {
let key = t.to_token_stream().to_string();
if seen.contains(&key) {
false
} else {
seen.push(key);
true
}
})
.collect()
}
fn op_path(op: &str) -> TS {
let op = Ident::new(op, proc_macro2::Span::call_site());
quote!(::alga2::op::#op)
}
fn trait_path(name: &str) -> TS {
let name = Ident::new(name, proc_macro2::Span::call_site());
quote!(::alga2::tower::#name)
}
struct Gen<'a> {
name: &'a Ident,
impl_g: &'a syn::ImplGenerics<'a>,
ty_g: &'a syn::TypeGenerics<'a>,
where_g: Option<&'a syn::WhereClause>,
}
impl Gen<'_> {
fn where_clause(&self, extra: &[TS]) -> TS {
let mut preds: Vec<syn::WherePredicate> = Vec::new();
if let Some(w) = self.where_g {
preds.extend(w.predicates.iter().cloned());
}
for e in extra {
preds.push(syn::parse_quote!(#e));
}
if preds.is_empty() {
quote!()
} else {
quote!(where #(#preds),*)
}
}
fn bounds(&self, fields: &[FieldInfo], trait_name: &str, op: &str) -> Vec<TS> {
let tr = trait_path(trait_name);
let op = op_path(op);
field_types(fields).iter().map(|ty| quote!(#ty: #tr<#op>)).collect()
}
fn single_op_chain(&self, fields: &[FieldInfo], op: &str, max_level: &str) -> TS {
let n = self.name;
let impl_g = self.impl_g;
let ty_g = self.ty_g;
let op_ts = op_path(op);
let mag = trait_path("Magma");
let semi = trait_path("Semigroup");
let quasi = trait_path("Quasigroup");
let mon = trait_path("Monoid");
let loop_t = trait_path("Loop");
let grp = trait_path("Group");
let abel = trait_path("AbelianGroup");
let combine_exprs = fields.iter().map(|f| {
let a = self_access(f);
let b = rhs_access(f);
let ty = &f.ty;
quote!(<#ty as #mag<#op_ts>>::combine(&#a, &#b))
}).collect::<Vec<_>>();
let identity_exprs = fields.iter().map(|f| {
let ty = &f.ty;
quote!(<#ty as #mon<#op_ts>>::identity())
}).collect::<Vec<_>>();
let inverse_exprs = fields.iter().map(|f| {
let a = self_access(f);
quote!(#a.inverse())
}).collect::<Vec<_>>();
let magma_b = self.bounds(fields, "Magma", op);
let monoid_b = self.bounds(fields, "Monoid", op);
let group_b = self.bounds(fields, "Group", op);
let w_magma = self.where_clause(&magma_b);
let w_monoid = self.where_clause(&monoid_b);
let w_group = self.where_clause(&group_b);
let c_combine = construct(fields, &combine_exprs);
let c_identity = construct(fields, &identity_exprs);
let c_inverse = construct(fields, &inverse_exprs);
let quasigroup_ladder = (op == "Additive").then(|| quote! {
impl #impl_g #quasi<#op_ts> for #n #ty_g
#w_magma
{}
impl #impl_g #loop_t<#op_ts> for #n #ty_g
#w_monoid
{}
});
let group_ladder = (max_level == "Group").then(|| quote! {
impl #impl_g #grp<#op_ts> for #n #ty_g
#w_group
{
fn inverse(&self) -> Self { #c_inverse }
}
impl #impl_g #abel<#op_ts> for #n #ty_g
#w_group
{}
});
quote! {
impl #impl_g #mag<#op_ts> for #n #ty_g
#w_magma
{
fn combine(&self, rhs: &Self) -> Self { #c_combine }
}
impl #impl_g #semi<#op_ts> for #n #ty_g
#w_magma
{}
#quasigroup_ladder
impl #impl_g #mon<#op_ts> for #n #ty_g
#w_monoid
{
fn identity() -> Self { #c_identity }
}
#group_ladder
}
}
fn two_op_chain(&self, fields: &[FieldInfo], level: Level) -> TS {
let n = self.name;
let impl_g = self.impl_g;
let ty_g = self.ty_g;
let semiring_b = field_types(fields).iter().map(|ty| {
quote!(#ty: ::alga2::tower::Semiring<::alga2::op::Additive, ::alga2::op::Multiplicative>)
}).collect::<Vec<_>>();
let field_b = field_types(fields).iter().map(|ty| {
quote!(#ty: ::alga2::tower::Field<::alga2::op::Additive, ::alga2::op::Multiplicative>)
}).collect::<Vec<_>>();
let comm_b = if level == Level::Field {
&field_b
} else {
&semiring_b
};
let w_semiring = self.where_clause(&semiring_b);
let w_comm = self.where_clause(comm_b);
let w_field = self.where_clause(&field_b);
let inv_exprs = fields.iter().map(|f| {
let a = self_access(f);
quote!(#a.inv())
}).collect::<Vec<_>>();
let c_inv = construct(fields, &inv_exprs);
let field_ladder = (level == Level::Field).then(|| quote! {
impl #impl_g ::alga2::tower::DivisionRing<::alga2::op::Additive, ::alga2::op::Multiplicative> for #n #ty_g
#w_field
{
fn inv(&self) -> Self { #c_inv }
}
impl #impl_g ::alga2::tower::Field<::alga2::op::Additive, ::alga2::op::Multiplicative> for #n #ty_g
#w_field
{}
});
quote! {
impl #impl_g ::alga2::tower::Semiring<::alga2::op::Additive, ::alga2::op::Multiplicative> for #n #ty_g
#w_semiring
{}
impl #impl_g ::alga2::tower::Ring<::alga2::op::Additive, ::alga2::op::Multiplicative> for #n #ty_g
#w_semiring
{}
impl #impl_g ::alga2::tower::CommutativeRing<::alga2::op::Additive, ::alga2::op::Multiplicative> for #n #ty_g
#w_comm
{}
#field_ladder
}
}
fn module_level(&self, fields: &[FieldInfo]) -> TS {
let n = self.name;
let impl_g = self.impl_g;
let ty_g = self.ty_g;
let first = fields.first().unwrap();
let module_b = field_types(fields).iter().map(|ty| {
quote!(#ty: ::alga2::tower::Module<::alga2::op::Additive, ::alga2::op::Multiplicative>)
}).collect::<Vec<_>>();
let w_module = self.where_clause(&module_b);
let scale_exprs = fields.iter().map(|f| {
let s_ = &f.suffix;
let a = quote!(v.#s_);
quote!(::alga2::tower::Module::<::alga2::op::Additive, ::alga2::op::Multiplicative>::scale(s, #a))
}).collect::<Vec<_>>();
let c_scale = construct(fields, &scale_exprs);
let ft = &first.ty;
quote! {
impl #impl_g ::alga2::tower::Module<::alga2::op::Additive, ::alga2::op::Multiplicative> for #n #ty_g
#w_module
{
type Scalar = <#ft as ::alga2::tower::Module<::alga2::op::Additive, ::alga2::op::Multiplicative>>::Scalar;
fn scale(s: &Self::Scalar, v: Self) -> Self { #c_scale }
}
}
}
}