#![deny(
clippy::unwrap_used,
clippy::expect_used,
clippy::panic,
missing_docs,
clippy::missing_errors_doc
)]
use proc_macro::TokenStream;
use quote::quote;
use syn::parse::{Parse, ParseStream};
use syn::{parse_macro_input, Ident, Result, Token};
struct ShapeArgs {
name: Ident,
left: Ident,
right: Ident,
}
impl Parse for ShapeArgs {
fn parse(input: ParseStream) -> Result<Self> {
let name: Ident = input.parse()?;
input.parse::<Token![,]>()?;
let left: Ident = input.parse()?;
input.parse::<Token![,]>()?;
let right: Ident = input.parse()?;
let _ = input.parse::<Token![,]>();
Ok(Self { name, left, right })
}
}
#[proc_macro]
pub fn product_shape(input: TokenStream) -> TokenStream {
let ShapeArgs { name, left, right } = parse_macro_input!(input as ShapeArgs);
let iri = format!(
"urn:uor:product:{}:{}",
lexically_earlier(&left, &right),
lexically_later(&left, &right)
);
let (l, r) = canonical_operand_pair(&left, &right);
let raw_const = format_ident_suffix(&name, "__CONSTRAINTS_RAW");
let len_const = format_ident_suffix(&name, "__CONSTRAINTS_LEN");
let expansion = quote! {
pub struct #name;
const #raw_const:
[::uor_foundation::pipeline::ConstraintRef;
2 * ::uor_foundation::enforcement::NERVE_CONSTRAINTS_CAP]
= ::uor_foundation::pipeline::sdk_concat_product_constraints::<#l, #r>();
const #len_const: usize =
::uor_foundation::pipeline::sdk_product_constraints_len::<#l, #r>();
impl ::uor_foundation::pipeline::ConstrainedTypeShape for #name {
const IRI: &'static str = #iri;
const SITE_BUDGET: usize =
<#l as ::uor_foundation::pipeline::ConstrainedTypeShape>::SITE_BUDGET
+ <#r as ::uor_foundation::pipeline::ConstrainedTypeShape>::SITE_BUDGET;
const SITE_COUNT: usize =
<#l as ::uor_foundation::pipeline::ConstrainedTypeShape>::SITE_COUNT
+ <#r as ::uor_foundation::pipeline::ConstrainedTypeShape>::SITE_COUNT;
const CONSTRAINTS: &'static [::uor_foundation::pipeline::ConstraintRef] = {
let buf: &'static [::uor_foundation::pipeline::ConstraintRef] = &#raw_const;
match buf.split_at_checked(#len_const) {
Some((head, _tail)) => head,
None => &[],
}
};
const CYCLE_SIZE: u64 = ::uor_foundation::pipeline::cycle_size_product(
<#l as ::uor_foundation::pipeline::ConstrainedTypeShape>::CYCLE_SIZE,
<#r as ::uor_foundation::pipeline::ConstrainedTypeShape>::CYCLE_SIZE,
);
}
impl ::uor_foundation::pipeline::__sdk_seal::Sealed for #name {}
impl ::uor_foundation::pipeline::IntoBindingValue for #name {
const MAX_BYTES: usize = 0;
fn into_binding_bytes(
&self,
_out: &mut [u8],
) -> ::core::result::Result<usize, ::uor_foundation::enforcement::ShapeViolation> {
Ok(0)
}
}
impl ::uor_foundation::enforcement::GroundedShape for #name {}
impl ::uor_foundation::pipeline::PartitionProductFields for #name {
const FIELDS: &'static [(u32, u32)] = &[
(0u32, <#l as ::uor_foundation::pipeline::ConstrainedTypeShape>::SITE_COUNT as u32),
(<#l as ::uor_foundation::pipeline::ConstrainedTypeShape>::SITE_COUNT as u32,
<#r as ::uor_foundation::pipeline::ConstrainedTypeShape>::SITE_COUNT as u32),
];
const FIELD_NAMES: &'static [&'static str] = &["", ""];
}
impl ::uor_foundation::pipeline::PartitionProductFactor<0> for #name {
type Factor = #l;
}
impl ::uor_foundation::pipeline::PartitionProductFactor<1> for #name {
type Factor = #r;
}
impl #name {
#[allow(clippy::too_many_arguments)]
pub fn mint_product_witness(
witt_bits: u16,
left_fingerprint: ::uor_foundation::ContentFingerprint,
right_fingerprint: ::uor_foundation::ContentFingerprint,
left_euler: i32,
right_euler: i32,
left_entropy_nats_bits: u64,
right_entropy_nats_bits: u64,
combined_euler: i32,
combined_entropy_nats_bits: u64,
combined_fingerprint: ::uor_foundation::ContentFingerprint,
) -> ::core::result::Result<
::uor_foundation::PartitionProductWitness,
::uor_foundation::enforcement::GenericImpossibilityWitness,
> {
use ::uor_foundation::pipeline::ConstrainedTypeShape;
let inputs = ::uor_foundation::PartitionProductMintInputs {
witt_bits,
left_fingerprint,
right_fingerprint,
left_site_budget: <#l as ConstrainedTypeShape>::SITE_BUDGET as u16,
right_site_budget: <#r as ConstrainedTypeShape>::SITE_BUDGET as u16,
left_total_site_count: <#l as ConstrainedTypeShape>::SITE_COUNT as u16,
right_total_site_count: <#r as ConstrainedTypeShape>::SITE_COUNT as u16,
left_euler,
right_euler,
left_entropy_nats_bits,
right_entropy_nats_bits,
combined_site_budget: <#name as ConstrainedTypeShape>::SITE_BUDGET as u16,
combined_site_count: <#name as ConstrainedTypeShape>::SITE_COUNT as u16,
combined_euler,
combined_entropy_nats_bits,
combined_fingerprint,
};
<::uor_foundation::PartitionProductWitness
as ::uor_foundation::VerifiedMint>::mint_verified(inputs)
}
}
};
expansion.into()
}
#[proc_macro]
pub fn coproduct_shape(input: TokenStream) -> TokenStream {
let ShapeArgs { name, left, right } = parse_macro_input!(input as ShapeArgs);
let iri = format!(
"urn:uor:coproduct:{}:{}",
lexically_earlier(&left, &right),
lexically_later(&left, &right)
);
let (l, r) = canonical_operand_pair(&left, &right);
let raw_const = format_ident_suffix(&name, "__CONSTRAINTS_RAW");
let len_const = format_ident_suffix(&name, "__CONSTRAINTS_LEN");
let tag_coeffs_l = format_ident_suffix(&name, "__TAG_COEFFS_L");
let tag_coeffs_r = format_ident_suffix(&name, "__TAG_COEFFS_R");
let tag_coeff_count = format_ident_suffix(&name, "__TAG_COEFF_COUNT");
let expansion = quote! {
pub struct #name;
const #tag_coeffs_l: [i64; ::uor_foundation::pipeline::AFFINE_MAX_COEFFS] = {
let mut out = [0i64; ::uor_foundation::pipeline::AFFINE_MAX_COEFFS];
let tag_site = {
let a = <#l as ::uor_foundation::pipeline::ConstrainedTypeShape>::SITE_COUNT;
let b = <#r as ::uor_foundation::pipeline::ConstrainedTypeShape>::SITE_COUNT;
if a > b { a } else { b }
};
if tag_site < ::uor_foundation::pipeline::AFFINE_MAX_COEFFS {
out[tag_site] = 1;
}
out
};
const #tag_coeffs_r: [i64; ::uor_foundation::pipeline::AFFINE_MAX_COEFFS] = #tag_coeffs_l;
const #tag_coeff_count: u32 = {
let a = <#l as ::uor_foundation::pipeline::ConstrainedTypeShape>::SITE_COUNT;
let b = <#r as ::uor_foundation::pipeline::ConstrainedTypeShape>::SITE_COUNT;
let tag_site = if a > b { a } else { b };
(tag_site as u32).saturating_add(1)
};
const #raw_const:
[::uor_foundation::pipeline::ConstraintRef;
2 * ::uor_foundation::enforcement::NERVE_CONSTRAINTS_CAP + 2]
= {
let mut out =
[::uor_foundation::pipeline::ConstraintRef::Site { position: u32::MAX };
2 * ::uor_foundation::enforcement::NERVE_CONSTRAINTS_CAP + 2];
let left_arr = <#l as ::uor_foundation::pipeline::ConstrainedTypeShape>::CONSTRAINTS;
let right_arr = <#r as ::uor_foundation::pipeline::ConstrainedTypeShape>::CONSTRAINTS;
let mut i = 0;
while i < left_arr.len() {
out[i] = left_arr[i];
i += 1;
}
out[i] = ::uor_foundation::pipeline::ConstraintRef::Affine {
coefficients: #tag_coeffs_l,
coefficient_count: #tag_coeff_count,
bias: 0,
};
let left_boundary = i + 1;
let mut j = 0;
while j < right_arr.len() {
out[left_boundary + j] = right_arr[j];
j += 1;
}
out[left_boundary + right_arr.len()] = ::uor_foundation::pipeline::ConstraintRef::Affine {
coefficients: #tag_coeffs_r,
coefficient_count: #tag_coeff_count,
bias: -1,
};
out
};
const #len_const: usize =
<#l as ::uor_foundation::pipeline::ConstrainedTypeShape>::CONSTRAINTS.len()
+ <#r as ::uor_foundation::pipeline::ConstrainedTypeShape>::CONSTRAINTS.len()
+ 2;
impl ::uor_foundation::pipeline::ConstrainedTypeShape for #name {
const IRI: &'static str = #iri;
const SITE_BUDGET: usize = {
let a = <#l as ::uor_foundation::pipeline::ConstrainedTypeShape>::SITE_BUDGET;
let b = <#r as ::uor_foundation::pipeline::ConstrainedTypeShape>::SITE_BUDGET;
if a > b { a } else { b }
};
const SITE_COUNT: usize = {
let a = <#l as ::uor_foundation::pipeline::ConstrainedTypeShape>::SITE_COUNT;
let b = <#r as ::uor_foundation::pipeline::ConstrainedTypeShape>::SITE_COUNT;
(if a > b { a } else { b }) + 1
};
const CONSTRAINTS: &'static [::uor_foundation::pipeline::ConstraintRef] = {
let buf: &'static [::uor_foundation::pipeline::ConstraintRef] = &#raw_const;
match buf.split_at_checked(#len_const) {
Some((head, _tail)) => head,
None => &[],
}
};
const CYCLE_SIZE: u64 = ::uor_foundation::pipeline::cycle_size_coproduct(
<#l as ::uor_foundation::pipeline::ConstrainedTypeShape>::CYCLE_SIZE,
<#r as ::uor_foundation::pipeline::ConstrainedTypeShape>::CYCLE_SIZE,
);
}
impl ::uor_foundation::pipeline::__sdk_seal::Sealed for #name {}
impl ::uor_foundation::pipeline::IntoBindingValue for #name {
const MAX_BYTES: usize = 0;
fn into_binding_bytes(
&self,
_out: &mut [u8],
) -> ::core::result::Result<usize, ::uor_foundation::enforcement::ShapeViolation> {
Ok(0)
}
}
impl ::uor_foundation::enforcement::GroundedShape for #name {}
impl #name {
#[allow(clippy::too_many_arguments)]
pub fn mint_coproduct_witness(
witt_bits: u16,
left_fingerprint: ::uor_foundation::ContentFingerprint,
right_fingerprint: ::uor_foundation::ContentFingerprint,
left_euler: i32,
right_euler: i32,
left_entropy_nats_bits: u64,
right_entropy_nats_bits: u64,
left_betti: [u32; ::uor_foundation::enforcement::MAX_BETTI_DIMENSION],
right_betti: [u32; ::uor_foundation::enforcement::MAX_BETTI_DIMENSION],
combined_euler: i32,
combined_entropy_nats_bits: u64,
combined_betti: [u32; ::uor_foundation::enforcement::MAX_BETTI_DIMENSION],
combined_fingerprint: ::uor_foundation::ContentFingerprint,
) -> ::core::result::Result<
::uor_foundation::PartitionCoproductWitness,
::uor_foundation::enforcement::GenericImpossibilityWitness,
> {
use ::uor_foundation::pipeline::ConstrainedTypeShape;
let left_total_site_count = <#l as ConstrainedTypeShape>::SITE_COUNT as u16;
let right_total_site_count = <#r as ConstrainedTypeShape>::SITE_COUNT as u16;
let tag_site = if left_total_site_count > right_total_site_count {
left_total_site_count
} else {
right_total_site_count
};
let left_constraint_count =
<#l as ConstrainedTypeShape>::CONSTRAINTS.len() + 1;
let inputs = ::uor_foundation::PartitionCoproductMintInputs {
witt_bits,
left_fingerprint,
right_fingerprint,
left_site_budget: <#l as ConstrainedTypeShape>::SITE_BUDGET as u16,
right_site_budget: <#r as ConstrainedTypeShape>::SITE_BUDGET as u16,
left_total_site_count,
right_total_site_count,
left_euler,
right_euler,
left_entropy_nats_bits,
right_entropy_nats_bits,
left_betti,
right_betti,
combined_site_budget: <#name as ConstrainedTypeShape>::SITE_BUDGET as u16,
combined_site_count: <#name as ConstrainedTypeShape>::SITE_COUNT as u16,
combined_euler,
combined_entropy_nats_bits,
combined_betti,
combined_fingerprint,
combined_constraints: <#name as ConstrainedTypeShape>::CONSTRAINTS,
left_constraint_count,
tag_site,
};
<::uor_foundation::PartitionCoproductWitness
as ::uor_foundation::VerifiedMint>::mint_verified(inputs)
}
}
};
expansion.into()
}
#[proc_macro]
pub fn cartesian_product_shape(input: TokenStream) -> TokenStream {
let ShapeArgs { name, left, right } = parse_macro_input!(input as ShapeArgs);
let iri = format!(
"urn:uor:cartesian:{}:{}",
lexically_earlier(&left, &right),
lexically_later(&left, &right)
);
let (l, r) = canonical_operand_pair(&left, &right);
let raw_const = format_ident_suffix(&name, "__CONSTRAINTS_RAW");
let len_const = format_ident_suffix(&name, "__CONSTRAINTS_LEN");
let expansion = quote! {
pub struct #name;
const #raw_const:
[::uor_foundation::pipeline::ConstraintRef;
2 * ::uor_foundation::enforcement::NERVE_CONSTRAINTS_CAP]
= ::uor_foundation::pipeline::sdk_concat_product_constraints::<#l, #r>();
const #len_const: usize =
::uor_foundation::pipeline::sdk_product_constraints_len::<#l, #r>();
impl ::uor_foundation::pipeline::ConstrainedTypeShape for #name {
const IRI: &'static str = #iri;
const SITE_BUDGET: usize =
<#l as ::uor_foundation::pipeline::ConstrainedTypeShape>::SITE_BUDGET
+ <#r as ::uor_foundation::pipeline::ConstrainedTypeShape>::SITE_BUDGET;
const SITE_COUNT: usize =
<#l as ::uor_foundation::pipeline::ConstrainedTypeShape>::SITE_COUNT
+ <#r as ::uor_foundation::pipeline::ConstrainedTypeShape>::SITE_COUNT;
const CONSTRAINTS: &'static [::uor_foundation::pipeline::ConstraintRef] = {
let buf: &'static [::uor_foundation::pipeline::ConstraintRef] = &#raw_const;
match buf.split_at_checked(#len_const) {
Some((head, _tail)) => head,
None => &[],
}
};
const CYCLE_SIZE: u64 = ::uor_foundation::pipeline::cycle_size_product(
<#l as ::uor_foundation::pipeline::ConstrainedTypeShape>::CYCLE_SIZE,
<#r as ::uor_foundation::pipeline::ConstrainedTypeShape>::CYCLE_SIZE,
);
}
impl ::uor_foundation::pipeline::CartesianProductShape for #name {
type Left = #l;
type Right = #r;
}
impl ::uor_foundation::pipeline::__sdk_seal::Sealed for #name {}
impl ::uor_foundation::pipeline::IntoBindingValue for #name {
const MAX_BYTES: usize = 0;
fn into_binding_bytes(
&self,
_out: &mut [u8],
) -> ::core::result::Result<usize, ::uor_foundation::enforcement::ShapeViolation> {
Ok(0)
}
}
impl ::uor_foundation::enforcement::GroundedShape for #name {}
impl ::uor_foundation::pipeline::PartitionProductFields for #name {
const FIELDS: &'static [(u32, u32)] = &[
(0u32, <#l as ::uor_foundation::pipeline::ConstrainedTypeShape>::SITE_COUNT as u32),
(<#l as ::uor_foundation::pipeline::ConstrainedTypeShape>::SITE_COUNT as u32,
<#r as ::uor_foundation::pipeline::ConstrainedTypeShape>::SITE_COUNT as u32),
];
const FIELD_NAMES: &'static [&'static str] = &["", ""];
}
impl ::uor_foundation::pipeline::PartitionProductFactor<0> for #name {
type Factor = #l;
}
impl ::uor_foundation::pipeline::PartitionProductFactor<1> for #name {
type Factor = #r;
}
impl #name {
#[allow(clippy::too_many_arguments)]
pub fn mint_cartesian_witness(
witt_bits: u16,
left_fingerprint: ::uor_foundation::ContentFingerprint,
right_fingerprint: ::uor_foundation::ContentFingerprint,
left_euler: i32,
right_euler: i32,
left_betti: [u32; ::uor_foundation::enforcement::MAX_BETTI_DIMENSION],
right_betti: [u32; ::uor_foundation::enforcement::MAX_BETTI_DIMENSION],
left_entropy_nats_bits: u64,
right_entropy_nats_bits: u64,
combined_euler: i32,
combined_betti: [u32; ::uor_foundation::enforcement::MAX_BETTI_DIMENSION],
combined_entropy_nats_bits: u64,
combined_fingerprint: ::uor_foundation::ContentFingerprint,
) -> ::core::result::Result<
::uor_foundation::CartesianProductWitness,
::uor_foundation::enforcement::GenericImpossibilityWitness,
> {
use ::uor_foundation::pipeline::ConstrainedTypeShape;
let inputs = ::uor_foundation::CartesianProductMintInputs {
witt_bits,
left_fingerprint,
right_fingerprint,
left_site_budget: <#l as ConstrainedTypeShape>::SITE_BUDGET as u16,
right_site_budget: <#r as ConstrainedTypeShape>::SITE_BUDGET as u16,
left_total_site_count: <#l as ConstrainedTypeShape>::SITE_COUNT as u16,
right_total_site_count: <#r as ConstrainedTypeShape>::SITE_COUNT as u16,
left_euler,
right_euler,
left_betti,
right_betti,
left_entropy_nats_bits,
right_entropy_nats_bits,
combined_site_budget: <#name as ConstrainedTypeShape>::SITE_BUDGET as u16,
combined_site_count: <#name as ConstrainedTypeShape>::SITE_COUNT as u16,
combined_euler,
combined_betti,
combined_entropy_nats_bits,
combined_fingerprint,
};
<::uor_foundation::CartesianProductWitness
as ::uor_foundation::VerifiedMint>::mint_verified(inputs)
}
}
};
expansion.into()
}
struct VariadicShapeArgs {
name: Ident,
operands: Vec<Ident>,
field_names: Vec<Option<Ident>>,
}
impl Parse for VariadicShapeArgs {
fn parse(input: ParseStream) -> Result<Self> {
let name: Ident = input.parse()?;
input.parse::<Token![,]>()?;
let mut operands: Vec<Ident> = Vec::new();
let mut field_names: Vec<Option<Ident>> = Vec::new();
fn parse_one(input: ParseStream) -> Result<(Option<Ident>, Ident)> {
let first: Ident = input.parse()?;
if input.peek(Token![:]) {
input.parse::<Token![:]>()?;
let ty: Ident = input.parse()?;
Ok((Some(first), ty))
} else {
Ok((None, first))
}
}
let (n0, t0) = parse_one(input)?;
field_names.push(n0);
operands.push(t0);
while input.peek(Token![,]) {
input.parse::<Token![,]>()?;
if input.is_empty() {
break;
}
let (ni, ti) = parse_one(input)?;
field_names.push(ni);
operands.push(ti);
}
if operands.len() < 2 {
return Err(syn::Error::new(
name.span(),
"partition_product!/partition_coproduct! require at least two operands",
));
}
let any_named = field_names.iter().any(|n| n.is_some());
let all_named = field_names.iter().all(|n| n.is_some());
if any_named && !all_named {
return Err(syn::Error::new(
name.span(),
"partition_product!/partition_coproduct!: named-field form must name every operand (or use the positional form for all)",
));
}
Ok(Self {
name,
operands,
field_names,
})
}
}
#[proc_macro]
pub fn partition_product(input: TokenStream) -> TokenStream {
let parsed = parse_macro_input!(input as VariadicShapeArgs);
expand_partition_product(parsed.name, &parsed.operands, &parsed.field_names)
}
fn expand_partition_product(
name: Ident,
operands: &[Ident],
field_names: &[Option<Ident>],
) -> TokenStream {
if operands.len() == 2 {
let l = &operands[0];
let r = &operands[1];
let iri = format!(
"urn:uor:product:{}:{}",
lexically_earlier(l, r),
lexically_later(l, r),
);
let (l, r) = canonical_operand_pair(l, r);
let raw_const = format_ident_suffix(&name, "__CONSTRAINTS_RAW");
let len_const = format_ident_suffix(&name, "__CONSTRAINTS_LEN");
let l_name_lit = field_names
.first()
.and_then(|n| n.as_ref())
.map(|i| i.to_string())
.unwrap_or_default();
let r_name_lit = field_names
.get(1)
.and_then(|n| n.as_ref())
.map(|i| i.to_string())
.unwrap_or_default();
let expansion = quote! {
pub struct #name;
const #raw_const:
[::uor_foundation::pipeline::ConstraintRef;
2 * ::uor_foundation::enforcement::NERVE_CONSTRAINTS_CAP]
= ::uor_foundation::pipeline::sdk_concat_product_constraints::<#l, #r>();
const #len_const: usize =
::uor_foundation::pipeline::sdk_product_constraints_len::<#l, #r>();
impl ::uor_foundation::pipeline::ConstrainedTypeShape for #name {
const IRI: &'static str = #iri;
const SITE_BUDGET: usize =
<#l as ::uor_foundation::pipeline::ConstrainedTypeShape>::SITE_BUDGET
+ <#r as ::uor_foundation::pipeline::ConstrainedTypeShape>::SITE_BUDGET;
const SITE_COUNT: usize =
<#l as ::uor_foundation::pipeline::ConstrainedTypeShape>::SITE_COUNT
+ <#r as ::uor_foundation::pipeline::ConstrainedTypeShape>::SITE_COUNT;
const CONSTRAINTS: &'static [::uor_foundation::pipeline::ConstraintRef] = {
let buf: &'static [::uor_foundation::pipeline::ConstraintRef] = &#raw_const;
match buf.split_at_checked(#len_const) {
Some((head, _tail)) => head,
None => &[],
}
};
const CYCLE_SIZE: u64 = ::uor_foundation::pipeline::cycle_size_product(
<#l as ::uor_foundation::pipeline::ConstrainedTypeShape>::CYCLE_SIZE,
<#r as ::uor_foundation::pipeline::ConstrainedTypeShape>::CYCLE_SIZE,
);
}
impl ::uor_foundation::pipeline::__sdk_seal::Sealed for #name {}
impl ::uor_foundation::pipeline::IntoBindingValue for #name {
const MAX_BYTES: usize = 0;
fn into_binding_bytes(
&self,
_out: &mut [u8],
) -> ::core::result::Result<usize, ::uor_foundation::enforcement::ShapeViolation> {
Ok(0)
}
}
impl ::uor_foundation::enforcement::GroundedShape for #name {}
impl ::uor_foundation::pipeline::PartitionProductFields for #name {
const FIELDS: &'static [(u32, u32)] = &[
(0u32, <#l as ::uor_foundation::pipeline::ConstrainedTypeShape>::SITE_COUNT as u32),
(<#l as ::uor_foundation::pipeline::ConstrainedTypeShape>::SITE_COUNT as u32,
<#r as ::uor_foundation::pipeline::ConstrainedTypeShape>::SITE_COUNT as u32),
];
const FIELD_NAMES: &'static [&'static str] = &[#l_name_lit, #r_name_lit];
}
impl ::uor_foundation::pipeline::PartitionProductFactor<0> for #name {
type Factor = #l;
}
impl ::uor_foundation::pipeline::PartitionProductFactor<1> for #name {
type Factor = #r;
}
};
expansion.into()
} else {
let any_named = field_names.iter().any(|n| n.is_some());
if any_named {
return expand_partition_product_named_flat(name, operands, field_names);
}
let mut intermediate_names: Vec<Ident> = Vec::with_capacity(operands.len() - 1);
for i in 0..operands.len() - 2 {
intermediate_names.push(Ident::new(&format!("{}PpStep{}", name, i), name.span()));
}
let mut chain: Vec<proc_macro2::TokenStream> = Vec::with_capacity(operands.len() - 1);
let mut prev = if operands.len() > 2 {
intermediate_names[0].clone()
} else {
name.clone()
};
let first_a = &operands[0];
let first_b = &operands[1];
let first_step_call = expand_partition_product_helper(prev.clone(), first_a, first_b);
chain.push(first_step_call);
for i in 2..operands.len() {
let next = if i == operands.len() - 1 {
name.clone()
} else {
intermediate_names[i - 1].clone()
};
let next_call = expand_partition_product_helper(next.clone(), &prev, &operands[i]);
chain.push(next_call);
prev = next;
}
let combined = quote! { #( #chain )* };
combined.into()
}
}
fn expand_partition_product_named_flat(
name: Ident,
operands: &[Ident],
field_names: &[Option<Ident>],
) -> TokenStream {
let mut fields_entries: Vec<proc_macro2::TokenStream> = Vec::with_capacity(operands.len());
for (i, op) in operands.iter().enumerate() {
let running_sum: proc_macro2::TokenStream = if i == 0 {
quote::quote! { 0u32 }
} else {
let prior = &operands[..i];
quote::quote! {
0u32 #(
+ <#prior as ::uor_foundation::pipeline::ConstrainedTypeShape>::SITE_COUNT as u32
)*
}
};
fields_entries.push(quote::quote! {
(#running_sum, <#op as ::uor_foundation::pipeline::ConstrainedTypeShape>::SITE_COUNT as u32)
});
}
let name_lits: Vec<String> = field_names
.iter()
.map(|n| n.as_ref().map(|i| i.to_string()).unwrap_or_default())
.collect();
let factor_impls: Vec<proc_macro2::TokenStream> = operands
.iter()
.enumerate()
.map(|(i, op)| {
quote::quote! {
impl ::uor_foundation::pipeline::PartitionProductFactor<#i> for #name {
type Factor = #op;
}
}
})
.collect();
let site_count_sum: proc_macro2::TokenStream = quote::quote! {
0usize #(
+ <#operands as ::uor_foundation::pipeline::ConstrainedTypeShape>::SITE_COUNT
)*
};
let site_budget_sum: proc_macro2::TokenStream = quote::quote! {
0usize #(
+ <#operands as ::uor_foundation::pipeline::ConstrainedTypeShape>::SITE_BUDGET
)*
};
let cycle_size_expr: proc_macro2::TokenStream = {
let mut acc = quote::quote! {
<#( #operands)* as ::uor_foundation::pipeline::ConstrainedTypeShape>::CYCLE_SIZE
};
acc = quote::quote! { 1u64 };
for op in operands {
acc = quote::quote! {
::uor_foundation::pipeline::cycle_size_product(
#acc,
<#op as ::uor_foundation::pipeline::ConstrainedTypeShape>::CYCLE_SIZE,
)
};
}
acc
};
let mut sorted: Vec<&Ident> = operands.iter().collect();
sorted.sort_by_key(|i| i.to_string());
let iri_body: String = sorted
.iter()
.map(|i| i.to_string())
.collect::<Vec<_>>()
.join(":");
let iri = format!("urn:uor:product:{iri_body}");
let raw_const = format_ident_suffix(&name, "__CONSTRAINTS_RAW");
let len_const = format_ident_suffix(&name, "__CONSTRAINTS_LEN");
let l = &operands[0];
let r = &operands[1];
let expansion = quote::quote! {
pub struct #name;
const #raw_const:
[::uor_foundation::pipeline::ConstraintRef;
2 * ::uor_foundation::enforcement::NERVE_CONSTRAINTS_CAP]
= ::uor_foundation::pipeline::sdk_concat_product_constraints::<#l, #r>();
const #len_const: usize =
::uor_foundation::pipeline::sdk_product_constraints_len::<#l, #r>();
impl ::uor_foundation::pipeline::ConstrainedTypeShape for #name {
const IRI: &'static str = #iri;
const SITE_BUDGET: usize = #site_budget_sum;
const SITE_COUNT: usize = #site_count_sum;
const CONSTRAINTS: &'static [::uor_foundation::pipeline::ConstraintRef] = {
let buf: &'static [::uor_foundation::pipeline::ConstraintRef] = &#raw_const;
match buf.split_at_checked(#len_const) {
Some((head, _tail)) => head,
None => &[],
}
};
const CYCLE_SIZE: u64 = #cycle_size_expr;
}
impl ::uor_foundation::pipeline::__sdk_seal::Sealed for #name {}
impl ::uor_foundation::pipeline::IntoBindingValue for #name {
const MAX_BYTES: usize = 0;
fn into_binding_bytes(
&self,
_out: &mut [u8],
) -> ::core::result::Result<usize, ::uor_foundation::enforcement::ShapeViolation> {
Ok(0)
}
}
impl ::uor_foundation::enforcement::GroundedShape for #name {}
impl ::uor_foundation::pipeline::PartitionProductFields for #name {
const FIELDS: &'static [(u32, u32)] = &[ #( #fields_entries ),* ];
const FIELD_NAMES: &'static [&'static str] = &[ #( #name_lits ),* ];
}
#( #factor_impls )*
};
expansion.into()
}
fn expand_partition_product_helper(
name: Ident,
left: &Ident,
right: &Ident,
) -> proc_macro2::TokenStream {
let iri = format!(
"urn:uor:product:{}:{}",
lexically_earlier(left, right),
lexically_later(left, right),
);
let (l, r) = canonical_operand_pair(left, right);
let raw_const = format_ident_suffix(&name, "__CONSTRAINTS_RAW");
let len_const = format_ident_suffix(&name, "__CONSTRAINTS_LEN");
quote! {
pub struct #name;
const #raw_const:
[::uor_foundation::pipeline::ConstraintRef;
2 * ::uor_foundation::enforcement::NERVE_CONSTRAINTS_CAP]
= ::uor_foundation::pipeline::sdk_concat_product_constraints::<#l, #r>();
const #len_const: usize =
::uor_foundation::pipeline::sdk_product_constraints_len::<#l, #r>();
impl ::uor_foundation::pipeline::ConstrainedTypeShape for #name {
const IRI: &'static str = #iri;
const SITE_BUDGET: usize =
<#l as ::uor_foundation::pipeline::ConstrainedTypeShape>::SITE_BUDGET
+ <#r as ::uor_foundation::pipeline::ConstrainedTypeShape>::SITE_BUDGET;
const SITE_COUNT: usize =
<#l as ::uor_foundation::pipeline::ConstrainedTypeShape>::SITE_COUNT
+ <#r as ::uor_foundation::pipeline::ConstrainedTypeShape>::SITE_COUNT;
const CONSTRAINTS: &'static [::uor_foundation::pipeline::ConstraintRef] = {
let buf: &'static [::uor_foundation::pipeline::ConstraintRef] = &#raw_const;
match buf.split_at_checked(#len_const) {
Some((head, _tail)) => head,
None => &[],
}
};
const CYCLE_SIZE: u64 = ::uor_foundation::pipeline::cycle_size_product(
<#l as ::uor_foundation::pipeline::ConstrainedTypeShape>::CYCLE_SIZE,
<#r as ::uor_foundation::pipeline::ConstrainedTypeShape>::CYCLE_SIZE,
);
}
impl ::uor_foundation::pipeline::__sdk_seal::Sealed for #name {}
impl ::uor_foundation::pipeline::IntoBindingValue for #name {
const MAX_BYTES: usize = 0;
fn into_binding_bytes(
&self,
_out: &mut [u8],
) -> ::core::result::Result<usize, ::uor_foundation::enforcement::ShapeViolation> {
Ok(0)
}
}
impl ::uor_foundation::enforcement::GroundedShape for #name {}
impl ::uor_foundation::pipeline::PartitionProductFields for #name {
const FIELDS: &'static [(u32, u32)] = &[
(0u32, <#l as ::uor_foundation::pipeline::ConstrainedTypeShape>::SITE_COUNT as u32),
(<#l as ::uor_foundation::pipeline::ConstrainedTypeShape>::SITE_COUNT as u32,
<#r as ::uor_foundation::pipeline::ConstrainedTypeShape>::SITE_COUNT as u32),
];
const FIELD_NAMES: &'static [&'static str] = &["", ""];
}
impl ::uor_foundation::pipeline::PartitionProductFactor<0> for #name {
type Factor = #l;
}
impl ::uor_foundation::pipeline::PartitionProductFactor<1> for #name {
type Factor = #r;
}
}
}
#[proc_macro]
pub fn partition_coproduct(input: TokenStream) -> TokenStream {
let parsed = parse_macro_input!(input as VariadicShapeArgs);
if parsed.field_names.iter().any(|n| n.is_some()) {
return syn::Error::new(
parsed.name.span(),
"partition_coproduct! does not accept named-field form (coproducts discriminate by tag, not by named field; ADR-033 G3 named-field form applies to partition_product!)",
)
.to_compile_error()
.into();
}
expand_partition_coproduct(parsed.name, &parsed.operands)
}
fn expand_partition_coproduct(name: Ident, operands: &[Ident]) -> TokenStream {
if operands.len() == 2 {
let combined = expand_partition_coproduct_helper(name, &operands[0], &operands[1]);
return combined.into();
}
let mut intermediate_names: Vec<Ident> = Vec::with_capacity(operands.len() - 1);
for i in 0..operands.len() - 2 {
intermediate_names.push(Ident::new(&format!("{}PcStep{}", name, i), name.span()));
}
let mut chain: Vec<proc_macro2::TokenStream> = Vec::with_capacity(operands.len() - 1);
let mut prev = if operands.len() > 2 {
intermediate_names[0].clone()
} else {
name.clone()
};
let first_step = expand_partition_coproduct_helper(prev.clone(), &operands[0], &operands[1]);
chain.push(first_step);
for i in 2..operands.len() {
let next = if i == operands.len() - 1 {
name.clone()
} else {
intermediate_names[i - 1].clone()
};
let step = expand_partition_coproduct_helper(next.clone(), &prev, &operands[i]);
chain.push(step);
prev = next;
}
let combined = quote! { #( #chain )* };
combined.into()
}
fn expand_partition_coproduct_helper(
name: Ident,
left: &Ident,
right: &Ident,
) -> proc_macro2::TokenStream {
let iri = format!(
"urn:uor:coproduct:{}:{}",
lexically_earlier(left, right),
lexically_later(left, right),
);
let (l, r) = canonical_operand_pair(left, right);
let raw_const = format_ident_suffix(&name, "__CONSTRAINTS_RAW");
let len_const = format_ident_suffix(&name, "__CONSTRAINTS_LEN");
let tag_coeffs_l = format_ident_suffix(&name, "__TAG_COEFFS_L");
let tag_coeffs_r = format_ident_suffix(&name, "__TAG_COEFFS_R");
let tag_coeff_count = format_ident_suffix(&name, "__TAG_COEFF_COUNT");
quote! {
pub struct #name;
const #tag_coeffs_l: [i64; ::uor_foundation::pipeline::AFFINE_MAX_COEFFS] = {
let mut out = [0i64; ::uor_foundation::pipeline::AFFINE_MAX_COEFFS];
let tag_site = {
let a = <#l as ::uor_foundation::pipeline::ConstrainedTypeShape>::SITE_COUNT;
let b = <#r as ::uor_foundation::pipeline::ConstrainedTypeShape>::SITE_COUNT;
if a > b { a } else { b }
};
if tag_site < ::uor_foundation::pipeline::AFFINE_MAX_COEFFS {
out[tag_site] = 1;
}
out
};
const #tag_coeffs_r: [i64; ::uor_foundation::pipeline::AFFINE_MAX_COEFFS] = #tag_coeffs_l;
const #tag_coeff_count: u32 = {
let a = <#l as ::uor_foundation::pipeline::ConstrainedTypeShape>::SITE_COUNT;
let b = <#r as ::uor_foundation::pipeline::ConstrainedTypeShape>::SITE_COUNT;
let tag_site = if a > b { a } else { b };
(tag_site as u32).saturating_add(1)
};
const #raw_const:
[::uor_foundation::pipeline::ConstraintRef;
2 * ::uor_foundation::enforcement::NERVE_CONSTRAINTS_CAP + 2]
= {
let mut out =
[::uor_foundation::pipeline::ConstraintRef::Site { position: u32::MAX };
2 * ::uor_foundation::enforcement::NERVE_CONSTRAINTS_CAP + 2];
let left_arr = <#l as ::uor_foundation::pipeline::ConstrainedTypeShape>::CONSTRAINTS;
let right_arr = <#r as ::uor_foundation::pipeline::ConstrainedTypeShape>::CONSTRAINTS;
let mut i = 0;
while i < left_arr.len() {
out[i] = left_arr[i];
i += 1;
}
out[i] = ::uor_foundation::pipeline::ConstraintRef::Affine {
coefficients: #tag_coeffs_l,
coefficient_count: #tag_coeff_count,
bias: 0,
};
i += 1;
let mut j = 0;
while j < right_arr.len() {
out[i] = right_arr[j];
i += 1;
j += 1;
}
out[i] = ::uor_foundation::pipeline::ConstraintRef::Affine {
coefficients: #tag_coeffs_r,
coefficient_count: #tag_coeff_count,
bias: -1,
};
out
};
const #len_const: usize =
<#l as ::uor_foundation::pipeline::ConstrainedTypeShape>::CONSTRAINTS.len()
+ <#r as ::uor_foundation::pipeline::ConstrainedTypeShape>::CONSTRAINTS.len()
+ 2;
impl ::uor_foundation::pipeline::ConstrainedTypeShape for #name {
const IRI: &'static str = #iri;
const SITE_BUDGET: usize = {
let a = <#l as ::uor_foundation::pipeline::ConstrainedTypeShape>::SITE_BUDGET;
let b = <#r as ::uor_foundation::pipeline::ConstrainedTypeShape>::SITE_BUDGET;
if a > b { a } else { b }
};
const SITE_COUNT: usize = {
let a = <#l as ::uor_foundation::pipeline::ConstrainedTypeShape>::SITE_COUNT;
let b = <#r as ::uor_foundation::pipeline::ConstrainedTypeShape>::SITE_COUNT;
(if a > b { a } else { b }) + 1
};
const CONSTRAINTS: &'static [::uor_foundation::pipeline::ConstraintRef] = {
let buf: &'static [::uor_foundation::pipeline::ConstraintRef] = &#raw_const;
match buf.split_at_checked(#len_const) {
Some((head, _tail)) => head,
None => &[],
}
};
const CYCLE_SIZE: u64 = ::uor_foundation::pipeline::cycle_size_coproduct(
<#l as ::uor_foundation::pipeline::ConstrainedTypeShape>::CYCLE_SIZE,
<#r as ::uor_foundation::pipeline::ConstrainedTypeShape>::CYCLE_SIZE,
);
}
impl ::uor_foundation::pipeline::__sdk_seal::Sealed for #name {}
impl ::uor_foundation::pipeline::IntoBindingValue for #name {
const MAX_BYTES: usize = 0;
fn into_binding_bytes(
&self,
_out: &mut [u8],
) -> ::core::result::Result<usize, ::uor_foundation::enforcement::ShapeViolation> {
Ok(0)
}
}
impl ::uor_foundation::enforcement::GroundedShape for #name {}
}
}
struct PrismModelInput {
model_vis: syn::Visibility,
model_name: Ident,
route_vis: syn::Visibility,
route_name: Ident,
h_ty: syn::Type,
b_ty: syn::Type,
a_ty: syn::Type,
r_ty: Option<syn::Type>,
input_ty: syn::Type,
output_ty: syn::Type,
route_input_ident: Ident,
route_body: syn::Block,
resolvers_body: Option<syn::Block>,
}
impl Parse for PrismModelInput {
fn parse(input: ParseStream) -> Result<Self> {
let model_vis: syn::Visibility = input.parse()?;
input.parse::<Token![struct]>()?;
let model_name: Ident = input.parse()?;
input.parse::<Token![;]>()?;
let route_vis: syn::Visibility = input.parse()?;
input.parse::<Token![struct]>()?;
let route_name: Ident = input.parse()?;
input.parse::<Token![;]>()?;
input.parse::<Token![impl]>()?;
let trait_ident: Ident = input.parse()?;
if trait_ident != "PrismModel" {
return Err(syn::Error::new(
trait_ident.span(),
"prism_model! expects an `impl PrismModel<H, B, A[, R]> for <Model>` block",
));
}
input.parse::<Token![<]>()?;
let h_ty: syn::Type = input.parse()?;
input.parse::<Token![,]>()?;
let b_ty: syn::Type = input.parse()?;
input.parse::<Token![,]>()?;
let a_ty: syn::Type = input.parse()?;
let r_ty: Option<syn::Type> = if input.peek(Token![,]) {
input.parse::<Token![,]>()?;
Some(input.parse::<syn::Type>()?)
} else {
None
};
input.parse::<Token![>]>()?;
input.parse::<Token![for]>()?;
let impl_target: Ident = input.parse()?;
if impl_target != model_name {
return Err(syn::Error::new(
impl_target.span(),
"prism_model!'s `impl PrismModel<…> for <Model>` target must match the declared model struct",
));
}
let body;
syn::braced!(body in input);
body.parse::<Token![type]>()?;
let input_kw: Ident = body.parse()?;
if input_kw != "Input" {
return Err(syn::Error::new(
input_kw.span(),
"expected `type Input = …;`",
));
}
body.parse::<Token![=]>()?;
let input_ty: syn::Type = body.parse()?;
body.parse::<Token![;]>()?;
body.parse::<Token![type]>()?;
let output_kw: Ident = body.parse()?;
if output_kw != "Output" {
return Err(syn::Error::new(
output_kw.span(),
"expected `type Output = …;`",
));
}
body.parse::<Token![=]>()?;
let output_ty: syn::Type = body.parse()?;
body.parse::<Token![;]>()?;
body.parse::<Token![type]>()?;
let route_kw: Ident = body.parse()?;
if route_kw != "Route" {
return Err(syn::Error::new(
route_kw.span(),
"expected `type Route = …;`",
));
}
body.parse::<Token![=]>()?;
let route_ty_ident: Ident = body.parse()?;
if route_ty_ident != route_name {
return Err(syn::Error::new(
route_ty_ident.span(),
"prism_model!'s `type Route = <RouteName>;` must match the declared route struct",
));
}
body.parse::<Token![;]>()?;
body.parse::<Token![fn]>()?;
let fn_kw: Ident = body.parse()?;
if fn_kw != "route" {
return Err(syn::Error::new(
fn_kw.span(),
"expected `fn route(input: Self::Input) -> Self::Output { … }`",
));
}
let params;
syn::parenthesized!(params in body);
let route_input_ident: Ident = params.parse()?;
params.parse::<Token![:]>()?;
let _input_param_ty: syn::Type = params.parse()?;
body.parse::<Token![->]>()?;
let _output_param_ty: syn::Type = body.parse()?;
let route_body: syn::Block = body.parse()?;
let resolvers_body: Option<syn::Block> = if body.peek(Token![fn]) {
body.parse::<Token![fn]>()?;
let resolvers_kw: Ident = body.parse()?;
if resolvers_kw != "resolvers" {
return Err(syn::Error::new(
resolvers_kw.span(),
"the only optional method after `fn route` is `fn resolvers() -> R { … }` (ADR-036)",
));
}
let resolvers_params;
syn::parenthesized!(resolvers_params in body);
if !resolvers_params.is_empty() {
return Err(syn::Error::new(
resolvers_kw.span(),
"`fn resolvers()` takes no parameters — its return value is the per-call ResolverTuple instance",
));
}
body.parse::<Token![->]>()?;
let _resolvers_ret_ty: syn::Type = body.parse()?;
let block: syn::Block = body.parse()?;
Some(block)
} else {
None
};
Ok(Self {
model_vis,
model_name,
route_vis,
route_name,
h_ty,
b_ty,
a_ty,
r_ty,
input_ty,
output_ty,
route_input_ident,
route_body,
resolvers_body,
})
}
}
enum TermSpec {
Literal(u64),
Variable,
Application {
operator: proc_macro2::TokenStream,
args_start: u32,
args_len: u32,
},
AxisInvocation {
axis_index: u32,
kernel_id: u32,
input_index: u32,
},
ProjectField {
source_index: u32,
byte_offset: proc_macro2::TokenStream,
byte_length: proc_macro2::TokenStream,
},
FirstAdmit {
domain_size_index: u32,
predicate_index: u32,
},
FirstAdmitIdxPlaceholder,
RecurseIdxPlaceholder,
Nerve { value_index: u32 },
ChainComplex { simplicial_index: u32 },
HomologyGroups { chain_index: u32 },
Betti { homology_index: u32 },
CochainComplex { chain_index: u32 },
CohomologyGroups { cochain_index: u32 },
PostnikovTower { simplicial_index: u32 },
HomotopyGroups { postnikov_index: u32 },
KInvariants { homotopy_index: u32 },
LiteralExpr {
value: proc_macro2::TokenStream,
level: proc_macro2::TokenStream,
},
VerbSplice {
arg_root_idx: u32,
fragment_path: proc_macro2::TokenStream,
},
Lift {
operand_index: u32,
target_witt: proc_macro2::TokenStream,
},
Project {
operand_index: u32,
target_witt: proc_macro2::TokenStream,
},
Try { body_index: u32 },
Recurse {
measure_index: u32,
base_index: u32,
step_index: u32,
},
Unfold { seed_index: u32, step_index: u32 },
Match {
scrutinee_index: u32,
arms_start: u32,
arms_len: u32,
},
WildcardSentinel,
RecursePlaceholder,
UnfoldPlaceholder,
}
#[derive(Default, Clone)]
struct BindingScope {
bindings: Vec<(Ident, usize)>,
route_input_ty: Option<syn::Type>,
}
impl BindingScope {
fn lookup(&self, ident: &Ident) -> Option<usize> {
self.bindings
.iter()
.rev()
.find(|(name, _)| name == ident)
.map(|(_, idx)| *idx)
}
fn shadow_check(&self, ident: &Ident) -> Result<()> {
if self.bindings.iter().any(|(name, _)| name == ident) {
return Err(syn::Error::new(
ident.span(),
format!(
"closure violation: shadowing `{ident}` (ADR-022 D3 G10 forbids declaring two `let`s with the same identifier in the same scope)"
),
));
}
Ok(())
}
fn push(&mut self, ident: Ident, root_idx: usize) {
self.bindings.push((ident, root_idx));
}
}
fn emit_term_for_expr(
expr: &syn::Expr,
route_input: &Ident,
arena: &mut Vec<TermSpec>,
scope: &mut BindingScope,
) -> Result<usize> {
match expr {
syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::Int(int_lit), .. }) => {
let value: u64 = int_lit.base10_parse().map_err(|e| {
syn::Error::new(int_lit.span(), format!("integer literal out of u64 range: {e}"))
})?;
let idx = arena.len();
arena.push(TermSpec::Literal(value));
Ok(idx)
}
syn::Expr::Path(path_expr) if path_expr.path.get_ident() == Some(route_input) => {
let idx = arena.len();
arena.push(TermSpec::Variable);
Ok(idx)
}
syn::Expr::Path(path_expr) => {
if let Some(name) = path_expr.path.get_ident() {
if let Some(root) = scope.lookup(name) {
return Ok(root);
}
}
Err(syn::Error::new_spanned(
path_expr,
"closure violation: identifier is not a foundation-vocabulary name (only the route's `input` parameter, `let`-introduced bindings, and reserved macro-vocabulary identifiers are recognised)",
))
}
syn::Expr::Call(call_expr) => emit_term_for_call(call_expr, route_input, arena, scope),
syn::Expr::Binary(bin_expr) => emit_term_for_binary(bin_expr, route_input, arena, scope),
syn::Expr::Block(block_expr) => emit_term_for_block(&block_expr.block, route_input, arena, scope),
syn::Expr::Paren(paren_expr) => {
emit_term_for_expr(&paren_expr.expr, route_input, arena, scope)
}
syn::Expr::Try(try_expr) => {
let body_root = emit_term_for_expr(&try_expr.expr, route_input, arena, scope)?;
let idx = arena.len();
arena.push(TermSpec::Try { body_index: body_root as u32 });
Ok(idx)
}
syn::Expr::Match(match_expr) => emit_term_for_match(match_expr, route_input, arena, scope),
syn::Expr::Field(field_expr) => emit_term_for_field(field_expr, route_input, arena, scope),
other => Err(syn::Error::new_spanned(
other,
"closure violation: expression form is not in foundation vocabulary (recognised forms: integer literals, the route's `input` parameter, `let`-introduced bindings, postfix `?`, `match`, field access on partition_product inputs, and macro-vocabulary function calls — PrimitiveOps, hash, lift, project, recurse, unfold, plus implementation verbs)",
)),
}
}
fn emit_term_for_field(
field_expr: &syn::ExprField,
route_input: &Ident,
arena: &mut Vec<TermSpec>,
scope: &mut BindingScope,
) -> Result<usize> {
let source_root = emit_term_for_expr(&field_expr.base, route_input, arena, scope)?;
let source_ty: syn::Type = resolve_field_receiver_type(&field_expr.base, route_input, scope)?;
let index_expr = field_index_expr(&field_expr.member, &source_ty);
let offset_expr = quote::quote! {
<#source_ty as ::uor_foundation::pipeline::PartitionProductFields>::FIELDS[#index_expr].0
};
let length_expr = quote::quote! {
<#source_ty as ::uor_foundation::pipeline::PartitionProductFields>::FIELDS[#index_expr].1
};
let idx = arena.len();
arena.push(TermSpec::ProjectField {
source_index: source_root as u32,
byte_offset: offset_expr,
byte_length: length_expr,
});
Ok(idx)
}
fn field_index_expr(member: &syn::Member, source_ty: &syn::Type) -> proc_macro2::TokenStream {
match member {
syn::Member::Unnamed(idx) => {
let i = idx.index as usize;
quote::quote! { #i }
}
syn::Member::Named(name) => {
let name_lit = name.to_string();
quote::quote! {
::uor_foundation::pipeline::field_index_by_name_in(
<#source_ty as ::uor_foundation::pipeline::PartitionProductFields>::FIELD_NAMES,
#name_lit,
)
}
}
}
}
fn resolve_field_receiver_type(
base: &syn::Expr,
route_input: &Ident,
scope: &BindingScope,
) -> Result<syn::Type> {
match base {
syn::Expr::Path(path_expr) if path_expr.path.get_ident() == Some(route_input) => {
match &scope.route_input_ty {
Some(ty) => Ok(ty.clone()),
None => Err(syn::Error::new_spanned(
base,
"closure violation: ADR-033 G20 field access requires the route input type to be known (only `prism_model!` and macros that pin `route_input_ty` admit field-access expressions)",
)),
}
}
syn::Expr::Paren(paren_expr) => {
resolve_field_receiver_type(&paren_expr.expr, route_input, scope)
}
syn::Expr::Field(inner) => {
let inner_ty = resolve_field_receiver_type(&inner.base, route_input, scope)?;
let inner_index = field_index_expr(&inner.member, &inner_ty);
let synth: syn::Type = syn::parse_quote! {
<#inner_ty as ::uor_foundation::pipeline::PartitionProductFactor<{#inner_index}>>::Factor
};
Ok(synth)
}
other => Err(syn::Error::new_spanned(
other,
"closure violation: ADR-033 G20 field access receiver must be the route's `input` parameter or another field-access (let-binding receivers are not supported)",
)),
}
}
fn emit_term_for_match(
match_expr: &syn::ExprMatch,
route_input: &Ident,
arena: &mut Vec<TermSpec>,
scope: &mut BindingScope,
) -> Result<usize> {
let scrutinee_root = emit_term_for_expr(&match_expr.expr, route_input, arena, scope)?;
if match_expr.arms.is_empty() {
return Err(syn::Error::new_spanned(
match_expr,
"closure violation: `match` (G6) must have at least one arm; non-exhaustive matches are closure violations",
));
}
let last_arm = &match_expr.arms[match_expr.arms.len() - 1];
let last_is_wildcard = matches!(last_arm.pat, syn::Pat::Wild(_));
if !last_is_wildcard {
return Err(syn::Error::new_spanned(
&last_arm.pat,
"closure violation: `match` (G6) MUST end with a wildcard arm `_ => <default>`; non-exhaustive matches are closure violations",
));
}
let mut arm_pairs: Vec<(TermSpec, usize)> = Vec::with_capacity(match_expr.arms.len());
for arm in &match_expr.arms {
if arm.guard.is_some() {
return Err(syn::Error::new_spanned(
arm,
"closure violation: match arm guards are not in the closure-body grammar (G6)",
));
}
let pattern_spec = match &arm.pat {
syn::Pat::Lit(lit_pat) => {
if let syn::Lit::Int(int_lit) = &lit_pat.lit {
let value: u64 = int_lit.base10_parse().map_err(|e| {
syn::Error::new(
int_lit.span(),
format!("integer literal out of u64 range: {e}"),
)
})?;
TermSpec::Literal(value)
} else {
return Err(syn::Error::new_spanned(
lit_pat,
"closure violation: match patterns must be integer literals or `_` (G6)",
));
}
}
syn::Pat::Wild(_) => TermSpec::WildcardSentinel,
other => {
return Err(syn::Error::new_spanned(
other,
"closure violation: match patterns must be integer literals or `_` (G6)",
));
}
};
let body_root = emit_term_for_expr(&arm.body, route_input, arena, scope)?;
arm_pairs.push((pattern_spec, body_root));
}
let arms_start = arena.len();
for (pattern_spec, body_root_idx) in arm_pairs {
arena.push(pattern_spec);
let body_copy = clone_term_spec(&arena[body_root_idx]);
arena.push(body_copy);
}
let arms_len = arena.len() - arms_start;
let idx = arena.len();
arena.push(TermSpec::Match {
scrutinee_index: scrutinee_root as u32,
arms_start: arms_start as u32,
arms_len: arms_len as u32,
});
Ok(idx)
}
fn emit_term_for_binary(
expr: &syn::ExprBinary,
route_input: &Ident,
arena: &mut Vec<TermSpec>,
scope: &mut BindingScope,
) -> Result<usize> {
let operator = match expr.op {
syn::BinOp::Le(_) => quote! { ::uor_foundation::PrimitiveOp::Le },
syn::BinOp::Lt(_) => quote! { ::uor_foundation::PrimitiveOp::Lt },
syn::BinOp::Ge(_) => quote! { ::uor_foundation::PrimitiveOp::Ge },
syn::BinOp::Gt(_) => quote! { ::uor_foundation::PrimitiveOp::Gt },
syn::BinOp::Add(_) => quote! { ::uor_foundation::PrimitiveOp::Add },
syn::BinOp::Sub(_) => quote! { ::uor_foundation::PrimitiveOp::Sub },
syn::BinOp::Mul(_) => quote! { ::uor_foundation::PrimitiveOp::Mul },
syn::BinOp::BitXor(_) => quote! { ::uor_foundation::PrimitiveOp::Xor },
syn::BinOp::BitAnd(_) => quote! { ::uor_foundation::PrimitiveOp::And },
syn::BinOp::BitOr(_) => quote! { ::uor_foundation::PrimitiveOp::Or },
_ => {
return Err(syn::Error::new_spanned(
expr,
"closure violation: binary operator is not in the closure-body grammar; recognised operators are arithmetic (+, -, *), bitwise (^, &, |), and byte-level comparison (<=, <, >=, >)",
));
}
};
let lhs_root = emit_term_for_expr(&expr.left, route_input, arena, scope)?;
let rhs_root = emit_term_for_expr(&expr.right, route_input, arena, scope)?;
let already_contiguous = rhs_root == lhs_root + 1;
let (args_start, args_len) = if already_contiguous {
(lhs_root as u32, 2u32)
} else {
let start = arena.len();
let lhs_dup = clone_term_spec(&arena[lhs_root]);
arena.push(lhs_dup);
let rhs_dup = clone_term_spec(&arena[rhs_root]);
arena.push(rhs_dup);
(start as u32, 2u32)
};
let idx = arena.len();
arena.push(TermSpec::Application {
operator,
args_start,
args_len,
});
Ok(idx)
}
fn clone_term_spec(spec: &TermSpec) -> TermSpec {
match spec {
TermSpec::Literal(v) => TermSpec::Literal(*v),
TermSpec::Variable => TermSpec::Variable,
TermSpec::Application {
operator,
args_start,
args_len,
} => TermSpec::Application {
operator: operator.clone(),
args_start: *args_start,
args_len: *args_len,
},
TermSpec::AxisInvocation {
axis_index,
kernel_id,
input_index,
} => TermSpec::AxisInvocation {
axis_index: *axis_index,
kernel_id: *kernel_id,
input_index: *input_index,
},
TermSpec::ProjectField {
source_index,
byte_offset,
byte_length,
} => TermSpec::ProjectField {
source_index: *source_index,
byte_offset: byte_offset.clone(),
byte_length: byte_length.clone(),
},
TermSpec::LiteralExpr { value, level } => TermSpec::LiteralExpr {
value: value.clone(),
level: level.clone(),
},
TermSpec::VerbSplice {
arg_root_idx,
fragment_path,
} => TermSpec::VerbSplice {
arg_root_idx: *arg_root_idx,
fragment_path: fragment_path.clone(),
},
TermSpec::Lift {
operand_index,
target_witt,
} => TermSpec::Lift {
operand_index: *operand_index,
target_witt: target_witt.clone(),
},
TermSpec::Project {
operand_index,
target_witt,
} => TermSpec::Project {
operand_index: *operand_index,
target_witt: target_witt.clone(),
},
TermSpec::Try { body_index } => TermSpec::Try {
body_index: *body_index,
},
TermSpec::Recurse {
measure_index,
base_index,
step_index,
} => TermSpec::Recurse {
measure_index: *measure_index,
base_index: *base_index,
step_index: *step_index,
},
TermSpec::Unfold {
seed_index,
step_index,
} => TermSpec::Unfold {
seed_index: *seed_index,
step_index: *step_index,
},
TermSpec::Match {
scrutinee_index,
arms_start,
arms_len,
} => TermSpec::Match {
scrutinee_index: *scrutinee_index,
arms_start: *arms_start,
arms_len: *arms_len,
},
TermSpec::WildcardSentinel => TermSpec::WildcardSentinel,
TermSpec::RecursePlaceholder => TermSpec::RecursePlaceholder,
TermSpec::UnfoldPlaceholder => TermSpec::UnfoldPlaceholder,
TermSpec::FirstAdmit {
domain_size_index,
predicate_index,
} => TermSpec::FirstAdmit {
domain_size_index: *domain_size_index,
predicate_index: *predicate_index,
},
TermSpec::FirstAdmitIdxPlaceholder => TermSpec::FirstAdmitIdxPlaceholder,
TermSpec::RecurseIdxPlaceholder => TermSpec::RecurseIdxPlaceholder,
TermSpec::Nerve { value_index } => TermSpec::Nerve {
value_index: *value_index,
},
TermSpec::ChainComplex { simplicial_index } => TermSpec::ChainComplex {
simplicial_index: *simplicial_index,
},
TermSpec::HomologyGroups { chain_index } => TermSpec::HomologyGroups {
chain_index: *chain_index,
},
TermSpec::Betti { homology_index } => TermSpec::Betti {
homology_index: *homology_index,
},
TermSpec::CochainComplex { chain_index } => TermSpec::CochainComplex {
chain_index: *chain_index,
},
TermSpec::CohomologyGroups { cochain_index } => TermSpec::CohomologyGroups {
cochain_index: *cochain_index,
},
TermSpec::PostnikovTower { simplicial_index } => TermSpec::PostnikovTower {
simplicial_index: *simplicial_index,
},
TermSpec::HomotopyGroups { postnikov_index } => TermSpec::HomotopyGroups {
postnikov_index: *postnikov_index,
},
TermSpec::KInvariants { homotopy_index } => TermSpec::KInvariants {
homotopy_index: *homotopy_index,
},
}
}
fn emit_term_for_block(
block: &syn::Block,
route_input: &Ident,
arena: &mut Vec<TermSpec>,
scope: &mut BindingScope,
) -> Result<usize> {
if block.stmts.is_empty() {
return Err(syn::Error::new_spanned(
block,
"closure violation: block expressions must contain at least one statement (G11) — empty blocks are unreachable in the closure-body grammar",
));
}
let mut local_scope = scope.clone();
let last = block.stmts.len() - 1;
for (i, stmt) in block.stmts.iter().enumerate() {
match stmt {
syn::Stmt::Local(local) => {
if i == last {
return Err(syn::Error::new_spanned(
stmt,
"closure violation: block must end with an expression statement (G11), not a `let` binding",
));
}
let ident = match &local.pat {
syn::Pat::Ident(pat_ident) => {
if pat_ident.by_ref.is_some() || pat_ident.mutability.is_some() {
return Err(syn::Error::new_spanned(
pat_ident,
"closure violation: `let` binding patterns must be plain identifiers (no `ref`, no `mut`) per ADR-022 D3 G10",
));
}
if pat_ident.subpat.is_some() {
return Err(syn::Error::new_spanned(
pat_ident,
"closure violation: `let` binding patterns must be plain identifiers per ADR-022 D3 G10",
));
}
pat_ident.ident.clone()
}
other => {
return Err(syn::Error::new_spanned(
other,
"closure violation: `let` binding patterns must be plain identifiers per ADR-022 D3 G10",
));
}
};
local_scope.shadow_check(&ident)?;
let init = local.init.as_ref().ok_or_else(|| {
syn::Error::new_spanned(
local,
"closure violation: `let` bindings must have an initializer (`let <name> = <expr>;`)",
)
})?;
if init.diverge.is_some() {
return Err(syn::Error::new_spanned(
local,
"closure violation: `let ... else` is not in the closure-body grammar (G10)",
));
}
let value_root =
emit_term_for_expr(&init.expr, route_input, arena, &mut local_scope)?;
local_scope.push(ident, value_root);
}
syn::Stmt::Expr(inner, semi) => {
if i == last {
if semi.is_some() {
return Err(syn::Error::new_spanned(
stmt,
"closure violation: block must end with a tail expression (G11), no trailing `;`",
));
}
return emit_term_for_expr(inner, route_input, arena, &mut local_scope);
}
return Err(syn::Error::new_spanned(
stmt,
"closure violation: only `let` statements may precede the block's tail expression (G10/G11)",
));
}
other => {
return Err(syn::Error::new_spanned(
other,
"closure violation: block statement is not in the closure-body grammar (G10/G11 admits only `let` and a final expression)",
));
}
}
}
Err(syn::Error::new_spanned(
block,
"closure violation: block lacks a tail expression (G11)",
))
}
fn emit_term_for_call(
call: &syn::ExprCall,
route_input: &Ident,
arena: &mut Vec<TermSpec>,
scope: &mut BindingScope,
) -> Result<usize> {
if let syn::Expr::Path(path_expr) = call.func.as_ref() {
let segments = &path_expr.path.segments;
if segments.len() == 1 {
let segment = &segments[0];
let last_ident = &segment.ident;
if last_ident == "lift" || last_ident == "project" {
let target_witt = match &segment.arguments {
syn::PathArguments::AngleBracketed(args) if args.args.len() == 1 => {
match &args.args[0] {
syn::GenericArgument::Type(syn::Type::Path(tp)) => tp.path.clone(),
other => {
return Err(syn::Error::new_spanned(
other,
"closure violation: lift/project's generic argument must be a Witt-level type (e.g., `WittLevel::W32`)",
));
}
}
}
_ => {
return Err(syn::Error::new_spanned(
segment,
format!(
"closure violation: `{last_ident}` requires a generic Witt-level argument: `{last_ident}::<WittLevel::W{{n}}>(operand)`"
),
));
}
};
if call.args.len() != 1 {
return Err(syn::Error::new(
last_ident.span(),
format!(
"closure violation: `{last_ident}` (G4/G5) expects 1 argument, got {}",
call.args.len()
),
));
}
let operand_root = emit_term_for_expr(&call.args[0], route_input, arena, scope)?;
let target_witt_ts = quote! { #target_witt };
let idx = arena.len();
let spec = if last_ident == "lift" {
TermSpec::Lift {
operand_index: operand_root as u32,
target_witt: target_witt_ts,
}
} else {
TermSpec::Project {
operand_index: operand_root as u32,
target_witt: target_witt_ts,
}
};
arena.push(spec);
return Ok(idx);
}
}
}
let func_ident = match call.func.as_ref() {
syn::Expr::Path(p) => p.path.get_ident().cloned().ok_or_else(|| {
syn::Error::new_spanned(
&call.func,
"closure violation: call target must be a bare identifier matching a PrimitiveOp name, the `hash` verb form, or a declared verb identifier",
)
})?,
other => {
return Err(syn::Error::new_spanned(
other,
"closure violation: call target must be a bare identifier",
));
}
};
let verb_resolution = match func_ident.to_string().as_str() {
"add" | "sub" | "mul" | "xor" | "and" | "or" | "neg" | "bnot" | "succ" | "pred"
| "hash" | "parallel" | "fold_n" | "tree_fold" | "first_admit"
| "recurse" | "unfold" | "concat"
| "nerve" | "chain_complex" | "homology_groups" | "betti" | "cochain_complex"
| "cohomology_groups" | "postnikov_tower" | "homotopy_groups" | "k_invariants" => false,
_ => true,
};
if verb_resolution {
if call.args.len() != 1 {
return Err(syn::Error::new(
func_ident.span(),
format!(
"verb invocation `{}` expects 1 argument (the verb's input value), got {}",
func_ident,
call.args.len()
),
));
}
let arg_root = emit_term_for_expr(&call.args[0], route_input, arena, scope)?;
let const_name = Ident::new(
&format!("VERB_TERMS_{}", to_screaming_snake(&func_ident.to_string())),
func_ident.span(),
);
let fragment_path = quote! { #const_name };
let idx = arena.len();
arena.push(TermSpec::VerbSplice {
arg_root_idx: arg_root as u32,
fragment_path,
});
return Ok(idx);
}
if func_ident == "fold_n" {
if call.args.len() != 3 {
return Err(syn::Error::new(
func_ident.span(),
format!(
"closure violation: `fold_n` (G14) expects 3 arguments (count, init, step closure), got {}",
call.args.len()
),
));
}
let count_lit: Option<u64> = match &call.args[0] {
syn::Expr::Lit(syn::ExprLit {
lit: syn::Lit::Int(int_lit),
..
}) => int_lit.base10_parse::<u64>().ok(),
_ => None,
};
let init_root = emit_term_for_expr(&call.args[1], route_input, arena, scope)?;
let step_closure = match &call.args[2] {
syn::Expr::Closure(c) => c,
other => {
return Err(syn::Error::new_spanned(
other,
"closure violation: `fold_n`'s third argument must be a closure `|state, idx| <step_expr>` (G14)",
));
}
};
if step_closure.inputs.len() != 2 {
return Err(syn::Error::new_spanned(
step_closure,
"closure violation: `fold_n`'s step closure expects exactly 2 parameters (state, idx) per G14",
));
}
let state_ident = match &step_closure.inputs[0] {
syn::Pat::Ident(p) => p.ident.clone(),
other => {
return Err(syn::Error::new_spanned(
other,
"closure violation: `fold_n`'s state parameter must be a plain identifier (G14)",
));
}
};
let idx_ident = match &step_closure.inputs[1] {
syn::Pat::Ident(p) => p.ident.clone(),
other => {
return Err(syn::Error::new_spanned(
other,
"closure violation: `fold_n`'s idx parameter must be a plain identifier (G14)",
));
}
};
const FOLD_UNROLL_THRESHOLD: u64 = 8;
if let Some(n) = count_lit {
if n <= FOLD_UNROLL_THRESHOLD {
let mut state_root = init_root;
for i in 0..n {
let idx_root = arena.len();
arena.push(TermSpec::Literal(i));
let mut iter_scope = scope.clone();
iter_scope.shadow_check(&state_ident)?;
iter_scope.shadow_check(&idx_ident)?;
iter_scope.push(state_ident.clone(), state_root);
iter_scope.push(idx_ident.clone(), idx_root);
state_root = emit_term_for_expr(
&step_closure.body,
route_input,
arena,
&mut iter_scope,
)?;
}
return Ok(state_root);
}
}
let measure_root = emit_term_for_expr(&call.args[0], route_input, arena, scope)?;
let mut step_scope = scope.clone();
step_scope.shadow_check(&state_ident)?;
step_scope.shadow_check(&idx_ident)?;
let placeholder_idx = arena.len();
arena.push(TermSpec::RecursePlaceholder);
step_scope.push(state_ident, placeholder_idx);
step_scope.push(idx_ident, measure_root);
let step_root =
emit_term_for_expr(&step_closure.body, route_input, arena, &mut step_scope)?;
let idx = arena.len();
arena.push(TermSpec::Recurse {
measure_index: measure_root as u32,
base_index: init_root as u32,
step_index: step_root as u32,
});
return Ok(idx);
}
if func_ident == "recurse" {
if call.args.len() != 3 {
return Err(syn::Error::new(
func_ident.span(),
format!(
"closure violation: `recurse` (G7) expects 3 arguments (measure, base, step closure), got {}",
call.args.len()
),
));
}
let measure_root = emit_term_for_expr(&call.args[0], route_input, arena, scope)?;
let base_root = emit_term_for_expr(&call.args[1], route_input, arena, scope)?;
let step_closure = match &call.args[2] {
syn::Expr::Closure(c) => c,
other => {
return Err(syn::Error::new_spanned(
other,
"closure violation: `recurse`'s third argument must be a closure `|self_ident| <step_expr>` (G7)",
));
}
};
if step_closure.inputs.is_empty() || step_closure.inputs.len() > 2 {
return Err(syn::Error::new_spanned(
step_closure,
"closure violation: `recurse`'s step closure expects 1 parameter (`|self_ident| <step>` per G7) or 2 parameters (`|self_ident, idx_ident| <step>` per ADR-034 Mechanism 1)",
));
}
let self_ident = match &step_closure.inputs[0] {
syn::Pat::Ident(p) => p.ident.clone(),
other => {
return Err(syn::Error::new_spanned(
other,
"closure violation: `recurse`'s step parameter must be a plain identifier (G7)",
));
}
};
let idx_ident_opt: Option<Ident> = if step_closure.inputs.len() == 2 {
match &step_closure.inputs[1] {
syn::Pat::Ident(p) => Some(p.ident.clone()),
other => {
return Err(syn::Error::new_spanned(
other,
"closure violation: `recurse`'s iteration-counter parameter must be a plain identifier (ADR-034 Mechanism 1)",
));
}
}
} else {
None
};
let mut step_scope = scope.clone();
step_scope.shadow_check(&self_ident)?;
let self_placeholder_idx = arena.len();
arena.push(TermSpec::RecursePlaceholder);
step_scope.push(self_ident, self_placeholder_idx);
if let Some(idx_ident) = idx_ident_opt {
step_scope.shadow_check(&idx_ident)?;
let idx_placeholder_idx = arena.len();
arena.push(TermSpec::RecurseIdxPlaceholder);
step_scope.push(idx_ident, idx_placeholder_idx);
}
let step_root =
emit_term_for_expr(&step_closure.body, route_input, arena, &mut step_scope)?;
let idx = arena.len();
arena.push(TermSpec::Recurse {
measure_index: measure_root as u32,
base_index: base_root as u32,
step_index: step_root as u32,
});
return Ok(idx);
}
if func_ident == "unfold" {
if call.args.len() != 2 {
return Err(syn::Error::new(
func_ident.span(),
format!(
"closure violation: `unfold` (G8) expects 2 arguments (seed, step closure), got {}",
call.args.len()
),
));
}
let seed_root = emit_term_for_expr(&call.args[0], route_input, arena, scope)?;
let step_closure = match &call.args[1] {
syn::Expr::Closure(c) => c,
other => {
return Err(syn::Error::new_spanned(
other,
"closure violation: `unfold`'s second argument must be a closure `|state_ident| <step_expr>` (G8)",
));
}
};
if step_closure.inputs.len() != 1 {
return Err(syn::Error::new_spanned(
step_closure,
"closure violation: `unfold`'s step closure expects exactly 1 parameter (the state placeholder, G8)",
));
}
let state_ident = match &step_closure.inputs[0] {
syn::Pat::Ident(p) => p.ident.clone(),
other => {
return Err(syn::Error::new_spanned(
other,
"closure violation: `unfold`'s step parameter must be a plain identifier (G8)",
));
}
};
let mut step_scope = scope.clone();
step_scope.shadow_check(&state_ident)?;
let placeholder_idx = arena.len();
arena.push(TermSpec::UnfoldPlaceholder);
step_scope.push(state_ident, placeholder_idx);
let step_root =
emit_term_for_expr(&step_closure.body, route_input, arena, &mut step_scope)?;
let idx = arena.len();
arena.push(TermSpec::Unfold {
seed_index: seed_root as u32,
step_index: step_root as u32,
});
return Ok(idx);
}
if func_ident == "parallel" {
if call.args.len() != 2 {
return Err(syn::Error::new(
func_ident.span(),
format!(
"closure violation: `parallel` (G13) expects 2 routes (left, right), got {}",
call.args.len()
),
));
}
let lhs_root = emit_term_for_expr(&call.args[0], route_input, arena, scope)?;
let rhs_root = emit_term_for_expr(&call.args[1], route_input, arena, scope)?;
let already_contiguous = rhs_root == lhs_root + 1;
let (args_start, args_len) = if already_contiguous {
(lhs_root as u32, 2u32)
} else {
let start = arena.len();
let lhs_dup = clone_term_spec(&arena[lhs_root]);
arena.push(lhs_dup);
let rhs_dup = clone_term_spec(&arena[rhs_root]);
arena.push(rhs_dup);
(start as u32, 2u32)
};
let idx = arena.len();
arena.push(TermSpec::Application {
operator: quote! { ::uor_foundation::PrimitiveOp::Or },
args_start,
args_len,
});
return Ok(idx);
}
if func_ident == "tree_fold" {
if call.args.len() != 2 {
return Err(syn::Error::new(
func_ident.span(),
format!(
"closure violation: `tree_fold` (G15) expects 2 arguments (reducer, leaves array), got {}",
call.args.len()
),
));
}
let reducer_ident = match &call.args[0] {
syn::Expr::Path(p) => p.path.get_ident().cloned().ok_or_else(|| {
syn::Error::new_spanned(
&call.args[0],
"closure violation: `tree_fold`'s reducer must be a bare identifier (PrimitiveOp or verb)",
)
})?,
other => {
return Err(syn::Error::new_spanned(
other,
"closure violation: `tree_fold`'s reducer must be a bare identifier",
));
}
};
let leaves_array = match &call.args[1] {
syn::Expr::Array(a) => a,
other => {
return Err(syn::Error::new_spanned(
other,
"closure violation: `tree_fold`'s leaves must be an array literal `[a, b, c, …]`",
));
}
};
if leaves_array.elems.is_empty() {
return Err(syn::Error::new_spanned(
leaves_array,
"closure violation: `tree_fold`'s leaves array must be non-empty (G15)",
));
}
let mut current_level: Vec<usize> = Vec::with_capacity(leaves_array.elems.len());
for leaf_expr in &leaves_array.elems {
current_level.push(emit_term_for_expr(leaf_expr, route_input, arena, scope)?);
}
let reducer_op_or_verb = match reducer_ident.to_string().as_str() {
"add" => Some(quote! { ::uor_foundation::PrimitiveOp::Add }),
"sub" => Some(quote! { ::uor_foundation::PrimitiveOp::Sub }),
"mul" => Some(quote! { ::uor_foundation::PrimitiveOp::Mul }),
"xor" => Some(quote! { ::uor_foundation::PrimitiveOp::Xor }),
"and" => Some(quote! { ::uor_foundation::PrimitiveOp::And }),
"or" => Some(quote! { ::uor_foundation::PrimitiveOp::Or }),
_ => None,
};
while current_level.len() > 1 {
let mut next_level: Vec<usize> = Vec::with_capacity(current_level.len().div_ceil(2));
let mut i = 0;
while i + 1 < current_level.len() {
let l_idx = current_level[i];
let r_idx = current_level[i + 1];
let already_contiguous = r_idx == l_idx + 1;
let (args_start, args_len) = if already_contiguous {
(l_idx as u32, 2u32)
} else {
let start = arena.len();
let l_dup = clone_term_spec(&arena[l_idx]);
arena.push(l_dup);
let r_dup = clone_term_spec(&arena[r_idx]);
arena.push(r_dup);
(start as u32, 2u32)
};
let app_idx = arena.len();
if let Some(op) = &reducer_op_or_verb {
arena.push(TermSpec::Application {
operator: op.clone(),
args_start,
args_len,
});
} else {
let const_name = Ident::new(
&format!(
"VERB_TERMS_{}",
to_screaming_snake(&reducer_ident.to_string())
),
reducer_ident.span(),
);
let fragment_path = quote! { #const_name };
arena.push(TermSpec::VerbSplice {
arg_root_idx: l_idx as u32,
fragment_path,
});
let _ = (args_start, args_len, r_idx);
}
next_level.push(app_idx);
i += 2;
}
if i < current_level.len() {
next_level.push(current_level[i]);
}
current_level = next_level;
}
return Ok(current_level[0]);
}
if func_ident == "first_admit" {
if call.args.len() != 2 {
return Err(syn::Error::new(
func_ident.span(),
format!(
"closure violation: `first_admit` (G16) expects 2 arguments (domain, predicate closure), got {}",
call.args.len()
),
));
}
let domain_arg = match &call.args[0] {
syn::Expr::Path(p) => p,
other => {
return Err(syn::Error::new_spanned(
other,
"closure violation: `first_admit`'s first argument must be a domain type path (e.g., `WittLevel::W8` or a `ConstrainedTypeShape`-implementing type)",
));
}
};
let pred_closure = match &call.args[1] {
syn::Expr::Closure(c) => c,
other => {
return Err(syn::Error::new_spanned(
other,
"closure violation: `first_admit`'s second argument must be a closure `|i| <predicate_body>` (G16)",
));
}
};
if pred_closure.inputs.len() != 1 {
return Err(syn::Error::new_spanned(
pred_closure,
"closure violation: `first_admit`'s predicate closure expects exactly 1 parameter (the iteration index, G16)",
));
}
let idx_ident = match &pred_closure.inputs[0] {
syn::Pat::Ident(p) => p.ident.clone(),
other => {
return Err(syn::Error::new_spanned(
other,
"closure violation: `first_admit`'s index parameter must be a plain identifier (G16)",
));
}
};
let domain_path = &domain_arg.path;
let domain_size_root = arena.len();
arena.push(TermSpec::LiteralExpr {
value: quote::quote! {
<#domain_path as ::uor_foundation::pipeline::ConstrainedTypeShape>::CYCLE_SIZE
},
level: quote::quote! { ::uor_foundation::WittLevel::new(64) },
});
let mut pred_scope = scope.clone();
pred_scope.shadow_check(&idx_ident)?;
let placeholder_idx = arena.len();
arena.push(TermSpec::FirstAdmitIdxPlaceholder);
pred_scope.push(idx_ident, placeholder_idx);
let pred_root =
emit_term_for_expr(&pred_closure.body, route_input, arena, &mut pred_scope)?;
let idx = arena.len();
arena.push(TermSpec::FirstAdmit {
domain_size_index: domain_size_root as u32,
predicate_index: pred_root as u32,
});
return Ok(idx);
}
if func_ident == "concat" {
if call.args.len() != 2 {
return Err(syn::Error::new(
func_ident.span(),
format!(
"closure violation: `concat` (substrate amendment) expects 2 arguments, got {}",
call.args.len()
),
));
}
let lhs_root = emit_term_for_expr(&call.args[0], route_input, arena, scope)?;
let rhs_root = emit_term_for_expr(&call.args[1], route_input, arena, scope)?;
let already_contiguous = rhs_root == lhs_root + 1;
let (args_start, args_len) = if already_contiguous {
(lhs_root as u32, 2u32)
} else {
let start = arena.len();
let lhs_dup = clone_term_spec(&arena[lhs_root]);
arena.push(lhs_dup);
let rhs_dup = clone_term_spec(&arena[rhs_root]);
arena.push(rhs_dup);
(start as u32, 2u32)
};
let idx = arena.len();
arena.push(TermSpec::Application {
operator: quote! { ::uor_foundation::PrimitiveOp::Concat },
args_start,
args_len,
});
return Ok(idx);
}
if func_ident == "hash" {
if call.args.len() != 1 {
return Err(syn::Error::new(
func_ident.span(),
format!(
"closure violation: `hash` (ADR-026 G19) expects 1 argument, got {}",
call.args.len()
),
));
}
let input_root = emit_term_for_expr(&call.args[0], route_input, arena, scope)?;
let idx = arena.len();
arena.push(TermSpec::AxisInvocation {
axis_index: 0,
kernel_id: 0,
input_index: input_root as u32,
});
return Ok(idx);
}
let psi_chain: &[(&str, &str)] = &[
("nerve", "G21"),
("chain_complex", "G22"),
("homology_groups", "G23"),
("betti", "G24"),
("cochain_complex", "G25"),
("cohomology_groups", "G26"),
("postnikov_tower", "G27"),
("homotopy_groups", "G28"),
("k_invariants", "G29"),
];
for (name, grammar) in psi_chain {
if func_ident == name {
if call.args.len() != 1 {
return Err(syn::Error::new(
func_ident.span(),
format!(
"closure violation: `{name}` (ADR-035 {grammar}) expects 1 argument, got {}",
call.args.len()
),
));
}
let operand_root = emit_term_for_expr(&call.args[0], route_input, arena, scope)?;
let idx = arena.len();
let spec = match *name {
"nerve" => TermSpec::Nerve {
value_index: operand_root as u32,
},
"chain_complex" => TermSpec::ChainComplex {
simplicial_index: operand_root as u32,
},
"homology_groups" => TermSpec::HomologyGroups {
chain_index: operand_root as u32,
},
"betti" => TermSpec::Betti {
homology_index: operand_root as u32,
},
"cochain_complex" => TermSpec::CochainComplex {
chain_index: operand_root as u32,
},
"cohomology_groups" => TermSpec::CohomologyGroups {
cochain_index: operand_root as u32,
},
"postnikov_tower" => TermSpec::PostnikovTower {
simplicial_index: operand_root as u32,
},
"homotopy_groups" => TermSpec::HomotopyGroups {
postnikov_index: operand_root as u32,
},
"k_invariants" => TermSpec::KInvariants {
homotopy_index: operand_root as u32,
},
_ => unreachable!(),
};
arena.push(spec);
return Ok(idx);
}
}
let (operator, expected_arity) = match func_ident.to_string().as_str() {
"add" => (quote! { ::uor_foundation::PrimitiveOp::Add }, 2usize),
"sub" => (quote! { ::uor_foundation::PrimitiveOp::Sub }, 2),
"mul" => (quote! { ::uor_foundation::PrimitiveOp::Mul }, 2),
"xor" => (quote! { ::uor_foundation::PrimitiveOp::Xor }, 2),
"and" => (quote! { ::uor_foundation::PrimitiveOp::And }, 2),
"or" => (quote! { ::uor_foundation::PrimitiveOp::Or }, 2),
"neg" => (quote! { ::uor_foundation::PrimitiveOp::Neg }, 1),
"bnot" => (quote! { ::uor_foundation::PrimitiveOp::Bnot }, 1),
"succ" => (quote! { ::uor_foundation::PrimitiveOp::Succ }, 1),
"pred" => (quote! { ::uor_foundation::PrimitiveOp::Pred }, 1),
"partition_product" | "partition_coproduct" => {
return Err(syn::Error::new(
func_ident.span(),
format!(
"closure violation: `{}` (ADR-026 G17/G18) is a type-level shape constructor — invoke it at item position via the named SDK form `partition_product!(<Name>, <A>, <B>)` or `partition_coproduct!(<Name>, <A>, <B>)`, then reference `<Name>` in `type Input` / `type Output`",
func_ident
),
));
}
other => {
return Err(syn::Error::new(
func_ident.span(),
format!(
"closure violation: `{other}` is not a foundation PrimitiveOp (recognised: add, sub, mul, xor, and, or, neg, bnot, succ, pred), nor an ADR-026 macro-vocabulary identifier (hash/parallel/fold_n/tree_fold/first_admit/recurse/unfold), nor a declared verb"
),
));
}
};
if call.args.len() != expected_arity {
return Err(syn::Error::new(
func_ident.span(),
format!(
"PrimitiveOp `{}` expects {} argument(s), got {}",
func_ident,
expected_arity,
call.args.len()
),
));
}
let mut arg_root_indices: Vec<usize> = Vec::with_capacity(call.args.len());
for arg in call.args.iter() {
arg_root_indices.push(emit_term_for_expr(arg, route_input, arena, scope)?);
}
let already_contiguous = arg_root_indices.windows(2).all(|w| w[1] == w[0] + 1);
let (args_start, args_len) = if already_contiguous && !arg_root_indices.is_empty() {
let len = arg_root_indices.len();
let start = arg_root_indices[0];
(start as u32, len as u32)
} else {
let start = arena.len();
for &idx in &arg_root_indices {
let dup = match &arena[idx] {
TermSpec::Literal(v) => TermSpec::Literal(*v),
TermSpec::Variable => TermSpec::Variable,
TermSpec::Application {
operator,
args_start,
args_len,
} => TermSpec::Application {
operator: operator.clone(),
args_start: *args_start,
args_len: *args_len,
},
TermSpec::AxisInvocation {
axis_index,
kernel_id,
input_index,
} => TermSpec::AxisInvocation {
axis_index: *axis_index,
kernel_id: *kernel_id,
input_index: *input_index,
},
TermSpec::ProjectField {
source_index,
byte_offset,
byte_length,
} => TermSpec::ProjectField {
source_index: *source_index,
byte_offset: byte_offset.clone(),
byte_length: byte_length.clone(),
},
TermSpec::LiteralExpr { value, level } => TermSpec::LiteralExpr {
value: value.clone(),
level: level.clone(),
},
TermSpec::VerbSplice {
arg_root_idx,
fragment_path,
} => TermSpec::VerbSplice {
arg_root_idx: *arg_root_idx,
fragment_path: fragment_path.clone(),
},
TermSpec::Lift {
operand_index,
target_witt,
} => TermSpec::Lift {
operand_index: *operand_index,
target_witt: target_witt.clone(),
},
TermSpec::Project {
operand_index,
target_witt,
} => TermSpec::Project {
operand_index: *operand_index,
target_witt: target_witt.clone(),
},
TermSpec::Try { body_index } => TermSpec::Try {
body_index: *body_index,
},
TermSpec::Recurse {
measure_index,
base_index,
step_index,
} => TermSpec::Recurse {
measure_index: *measure_index,
base_index: *base_index,
step_index: *step_index,
},
TermSpec::Unfold {
seed_index,
step_index,
} => TermSpec::Unfold {
seed_index: *seed_index,
step_index: *step_index,
},
TermSpec::Match {
scrutinee_index,
arms_start,
arms_len,
} => TermSpec::Match {
scrutinee_index: *scrutinee_index,
arms_start: *arms_start,
arms_len: *arms_len,
},
TermSpec::WildcardSentinel => TermSpec::WildcardSentinel,
TermSpec::RecursePlaceholder => TermSpec::RecursePlaceholder,
TermSpec::UnfoldPlaceholder => TermSpec::UnfoldPlaceholder,
TermSpec::FirstAdmit {
domain_size_index,
predicate_index,
} => TermSpec::FirstAdmit {
domain_size_index: *domain_size_index,
predicate_index: *predicate_index,
},
TermSpec::FirstAdmitIdxPlaceholder => TermSpec::FirstAdmitIdxPlaceholder,
TermSpec::RecurseIdxPlaceholder => TermSpec::RecurseIdxPlaceholder,
TermSpec::Nerve { value_index } => TermSpec::Nerve {
value_index: *value_index,
},
TermSpec::ChainComplex { simplicial_index } => TermSpec::ChainComplex {
simplicial_index: *simplicial_index,
},
TermSpec::HomologyGroups { chain_index } => TermSpec::HomologyGroups {
chain_index: *chain_index,
},
TermSpec::Betti { homology_index } => TermSpec::Betti {
homology_index: *homology_index,
},
TermSpec::CochainComplex { chain_index } => TermSpec::CochainComplex {
chain_index: *chain_index,
},
TermSpec::CohomologyGroups { cochain_index } => TermSpec::CohomologyGroups {
cochain_index: *cochain_index,
},
TermSpec::PostnikovTower { simplicial_index } => TermSpec::PostnikovTower {
simplicial_index: *simplicial_index,
},
TermSpec::HomotopyGroups { postnikov_index } => TermSpec::HomotopyGroups {
postnikov_index: *postnikov_index,
},
TermSpec::KInvariants { homotopy_index } => TermSpec::KInvariants {
homotopy_index: *homotopy_index,
},
};
arena.push(dup);
}
(start as u32, arg_root_indices.len() as u32)
};
let app_idx = arena.len();
arena.push(TermSpec::Application {
operator,
args_start,
args_len,
});
Ok(app_idx)
}
fn render_arena(arena: &[TermSpec]) -> Vec<proc_macro2::TokenStream> {
arena
.iter()
.map(|spec| match spec {
TermSpec::Literal(value) => quote! {
::uor_foundation::enforcement::Term::Literal {
value: #value,
level: ::uor_foundation::WittLevel::W8,
}
},
TermSpec::LiteralExpr { value, level } => quote! {
::uor_foundation::enforcement::Term::Literal {
value: #value,
level: #level,
}
},
TermSpec::Variable => quote! {
::uor_foundation::enforcement::Term::Variable { name_index: 0u32 }
},
TermSpec::Application {
operator,
args_start,
args_len,
} => {
let s = *args_start;
let l = *args_len;
quote! {
::uor_foundation::enforcement::Term::Application {
operator: #operator,
args: ::uor_foundation::enforcement::TermList {
start: #s,
len: #l,
},
}
}
}
TermSpec::AxisInvocation {
axis_index,
kernel_id,
input_index,
} => {
let a = *axis_index;
let k = *kernel_id;
let i = *input_index;
quote! {
::uor_foundation::enforcement::Term::AxisInvocation {
axis_index: #a,
kernel_id: #k,
input_index: #i,
}
}
}
TermSpec::ProjectField {
source_index,
byte_offset,
byte_length,
} => {
let s = *source_index;
quote! {
::uor_foundation::enforcement::Term::ProjectField {
source_index: #s,
byte_offset: (#byte_offset) as u32,
byte_length: (#byte_length) as u32,
}
}
}
TermSpec::VerbSplice { .. } => {
quote! {
compile_error!(
"internal error: VerbSplice reached the slice-literal renderer; \
render_const_fn_arena_builder should have been chosen"
)
}
}
TermSpec::Lift {
operand_index,
target_witt,
} => {
let i = *operand_index;
quote! {
::uor_foundation::enforcement::Term::Lift {
operand_index: #i,
target: #target_witt,
}
}
}
TermSpec::Project {
operand_index,
target_witt,
} => {
let i = *operand_index;
quote! {
::uor_foundation::enforcement::Term::Project {
operand_index: #i,
target: #target_witt,
}
}
}
TermSpec::Try { body_index } => {
let i = *body_index;
quote! {
::uor_foundation::enforcement::Term::Try {
body_index: #i,
handler_index: u32::MAX,
}
}
}
TermSpec::Recurse {
measure_index,
base_index,
step_index,
} => {
let m = *measure_index;
let b = *base_index;
let s = *step_index;
quote! {
::uor_foundation::enforcement::Term::Recurse {
measure_index: #m,
base_index: #b,
step_index: #s,
}
}
}
TermSpec::Unfold {
seed_index,
step_index,
} => {
let s = *seed_index;
let st = *step_index;
quote! {
::uor_foundation::enforcement::Term::Unfold {
seed_index: #s,
step_index: #st,
}
}
}
TermSpec::Match {
scrutinee_index,
arms_start,
arms_len,
} => {
let s = *scrutinee_index;
let st = *arms_start;
let l = *arms_len;
quote! {
::uor_foundation::enforcement::Term::Match {
scrutinee_index: #s,
arms: ::uor_foundation::enforcement::TermList {
start: #st,
len: #l,
},
}
}
}
TermSpec::WildcardSentinel => quote! {
::uor_foundation::enforcement::Term::Variable {
name_index: u32::MAX,
}
},
TermSpec::RecursePlaceholder => quote! {
::uor_foundation::enforcement::Term::Variable {
name_index: ::uor_foundation::pipeline::RECURSE_PLACEHOLDER_NAME_INDEX,
}
},
TermSpec::UnfoldPlaceholder => quote! {
::uor_foundation::enforcement::Term::Variable {
name_index: ::uor_foundation::pipeline::UNFOLD_PLACEHOLDER_NAME_INDEX,
}
},
TermSpec::FirstAdmit {
domain_size_index,
predicate_index,
} => {
let d = *domain_size_index;
let p = *predicate_index;
quote! {
::uor_foundation::enforcement::Term::FirstAdmit {
domain_size_index: #d,
predicate_index: #p,
}
}
}
TermSpec::FirstAdmitIdxPlaceholder => quote! {
::uor_foundation::enforcement::Term::Variable {
name_index: ::uor_foundation::pipeline::FIRST_ADMIT_IDX_NAME_INDEX,
}
},
TermSpec::RecurseIdxPlaceholder => quote! {
::uor_foundation::enforcement::Term::Variable {
name_index: ::uor_foundation::pipeline::RECURSE_IDX_NAME_INDEX,
}
},
TermSpec::Nerve { value_index } => {
let v = *value_index;
quote! {
::uor_foundation::enforcement::Term::Nerve { value_index: #v }
}
}
TermSpec::ChainComplex { simplicial_index } => {
let s = *simplicial_index;
quote! {
::uor_foundation::enforcement::Term::ChainComplex { simplicial_index: #s }
}
}
TermSpec::HomologyGroups { chain_index } => {
let c = *chain_index;
quote! {
::uor_foundation::enforcement::Term::HomologyGroups { chain_index: #c }
}
}
TermSpec::Betti { homology_index } => {
let h = *homology_index;
quote! {
::uor_foundation::enforcement::Term::Betti { homology_index: #h }
}
}
TermSpec::CochainComplex { chain_index } => {
let c = *chain_index;
quote! {
::uor_foundation::enforcement::Term::CochainComplex { chain_index: #c }
}
}
TermSpec::CohomologyGroups { cochain_index } => {
let c = *cochain_index;
quote! {
::uor_foundation::enforcement::Term::CohomologyGroups { cochain_index: #c }
}
}
TermSpec::PostnikovTower { simplicial_index } => {
let s = *simplicial_index;
quote! {
::uor_foundation::enforcement::Term::PostnikovTower { simplicial_index: #s }
}
}
TermSpec::HomotopyGroups { postnikov_index } => {
let p = *postnikov_index;
quote! {
::uor_foundation::enforcement::Term::HomotopyGroups { postnikov_index: #p }
}
}
TermSpec::KInvariants { homotopy_index } => {
let h = *homotopy_index;
quote! {
::uor_foundation::enforcement::Term::KInvariants { homotopy_index: #h }
}
}
})
.collect()
}
fn render_atomic_term_in_builder(
spec: &TermSpec,
spec_pos: &[proc_macro2::TokenStream],
) -> proc_macro2::TokenStream {
let pos_at = |idx: u32| -> proc_macro2::TokenStream {
let i = idx as usize;
if i < spec_pos.len() {
spec_pos[i].clone()
} else {
quote! { u32::MAX }
}
};
match spec {
TermSpec::Literal(value) => {
let v = *value;
quote! {
::uor_foundation::enforcement::Term::Literal {
value: #v,
level: ::uor_foundation::WittLevel::W8,
}
}
}
TermSpec::LiteralExpr { value, level } => quote! {
::uor_foundation::enforcement::Term::Literal {
value: #value,
level: #level,
}
},
TermSpec::Variable => quote! {
::uor_foundation::enforcement::Term::Variable { name_index: 0u32 }
},
TermSpec::Application {
operator,
args_start,
args_len,
} => {
let s = pos_at(*args_start);
let l = *args_len;
quote! {
::uor_foundation::enforcement::Term::Application {
operator: #operator,
args: ::uor_foundation::enforcement::TermList {
start: (#s) as u32,
len: #l,
},
}
}
}
TermSpec::AxisInvocation {
axis_index,
kernel_id,
input_index,
} => {
let a = *axis_index;
let k = *kernel_id;
let i = pos_at(*input_index);
quote! {
::uor_foundation::enforcement::Term::AxisInvocation {
axis_index: #a,
kernel_id: #k,
input_index: (#i) as u32,
}
}
}
TermSpec::ProjectField {
source_index,
byte_offset,
byte_length,
} => {
let s = pos_at(*source_index);
quote! {
::uor_foundation::enforcement::Term::ProjectField {
source_index: (#s) as u32,
byte_offset: (#byte_offset) as u32,
byte_length: (#byte_length) as u32,
}
}
}
TermSpec::Lift {
operand_index,
target_witt,
} => {
let i = pos_at(*operand_index);
quote! {
::uor_foundation::enforcement::Term::Lift {
operand_index: (#i) as u32,
target: #target_witt,
}
}
}
TermSpec::Project {
operand_index,
target_witt,
} => {
let i = pos_at(*operand_index);
quote! {
::uor_foundation::enforcement::Term::Project {
operand_index: (#i) as u32,
target: #target_witt,
}
}
}
TermSpec::Try { body_index } => {
let i = pos_at(*body_index);
quote! {
::uor_foundation::enforcement::Term::Try {
body_index: (#i) as u32,
handler_index: u32::MAX,
}
}
}
TermSpec::Recurse {
measure_index,
base_index,
step_index,
} => {
let m = pos_at(*measure_index);
let b = pos_at(*base_index);
let s = pos_at(*step_index);
quote! {
::uor_foundation::enforcement::Term::Recurse {
measure_index: (#m) as u32,
base_index: (#b) as u32,
step_index: (#s) as u32,
}
}
}
TermSpec::Unfold {
seed_index,
step_index,
} => {
let s = pos_at(*seed_index);
let st = pos_at(*step_index);
quote! {
::uor_foundation::enforcement::Term::Unfold {
seed_index: (#s) as u32,
step_index: (#st) as u32,
}
}
}
TermSpec::Match {
scrutinee_index,
arms_start,
arms_len,
} => {
let sc = pos_at(*scrutinee_index);
let st = *arms_start;
let l = *arms_len;
let st_pos = if (st as usize) < spec_pos.len() {
spec_pos[st as usize].clone()
} else {
quote! { u32::MAX }
};
quote! {
::uor_foundation::enforcement::Term::Match {
scrutinee_index: (#sc) as u32,
arms: ::uor_foundation::enforcement::TermList {
start: (#st_pos) as u32,
len: #l,
},
}
}
}
TermSpec::WildcardSentinel => quote! {
::uor_foundation::enforcement::Term::Variable { name_index: u32::MAX }
},
TermSpec::RecursePlaceholder => quote! {
::uor_foundation::enforcement::Term::Variable {
name_index: ::uor_foundation::pipeline::RECURSE_PLACEHOLDER_NAME_INDEX,
}
},
TermSpec::UnfoldPlaceholder => quote! {
::uor_foundation::enforcement::Term::Variable {
name_index: ::uor_foundation::pipeline::UNFOLD_PLACEHOLDER_NAME_INDEX,
}
},
TermSpec::FirstAdmit {
domain_size_index,
predicate_index,
} => {
let d = pos_at(*domain_size_index);
let p = pos_at(*predicate_index);
quote! {
::uor_foundation::enforcement::Term::FirstAdmit {
domain_size_index: (#d) as u32,
predicate_index: (#p) as u32,
}
}
}
TermSpec::FirstAdmitIdxPlaceholder => quote! {
::uor_foundation::enforcement::Term::Variable {
name_index: ::uor_foundation::pipeline::FIRST_ADMIT_IDX_NAME_INDEX,
}
},
TermSpec::RecurseIdxPlaceholder => quote! {
::uor_foundation::enforcement::Term::Variable {
name_index: ::uor_foundation::pipeline::RECURSE_IDX_NAME_INDEX,
}
},
TermSpec::Nerve { value_index } => {
let v = pos_at(*value_index);
quote! {
::uor_foundation::enforcement::Term::Nerve { value_index: (#v) as u32 }
}
}
TermSpec::ChainComplex { simplicial_index } => {
let s = pos_at(*simplicial_index);
quote! {
::uor_foundation::enforcement::Term::ChainComplex {
simplicial_index: (#s) as u32,
}
}
}
TermSpec::HomologyGroups { chain_index } => {
let c = pos_at(*chain_index);
quote! {
::uor_foundation::enforcement::Term::HomologyGroups {
chain_index: (#c) as u32,
}
}
}
TermSpec::Betti { homology_index } => {
let h = pos_at(*homology_index);
quote! {
::uor_foundation::enforcement::Term::Betti {
homology_index: (#h) as u32,
}
}
}
TermSpec::CochainComplex { chain_index } => {
let c = pos_at(*chain_index);
quote! {
::uor_foundation::enforcement::Term::CochainComplex {
chain_index: (#c) as u32,
}
}
}
TermSpec::CohomologyGroups { cochain_index } => {
let c = pos_at(*cochain_index);
quote! {
::uor_foundation::enforcement::Term::CohomologyGroups {
cochain_index: (#c) as u32,
}
}
}
TermSpec::PostnikovTower { simplicial_index } => {
let s = pos_at(*simplicial_index);
quote! {
::uor_foundation::enforcement::Term::PostnikovTower {
simplicial_index: (#s) as u32,
}
}
}
TermSpec::HomotopyGroups { postnikov_index } => {
let p = pos_at(*postnikov_index);
quote! {
::uor_foundation::enforcement::Term::HomotopyGroups {
postnikov_index: (#p) as u32,
}
}
}
TermSpec::KInvariants { homotopy_index } => {
let h = pos_at(*homotopy_index);
quote! {
::uor_foundation::enforcement::Term::KInvariants {
homotopy_index: (#h) as u32,
}
}
}
TermSpec::VerbSplice { .. } => quote! {
compile_error!("VerbSplice handled separately in render_const_fn_arena_builder")
},
}
}
fn render_const_fn_arena_builder(arena: &[TermSpec]) -> proc_macro2::TokenStream {
let spec_pos: Vec<proc_macro2::TokenStream> = (0..arena.len())
.map(|i| {
let id = Ident::new(&format!("pos_{}", i), proc_macro2::Span::call_site());
quote! { #id }
})
.collect();
let mut stmts: Vec<proc_macro2::TokenStream> = Vec::with_capacity(arena.len());
for (i, spec) in arena.iter().enumerate() {
let pos_id = &spec_pos[i];
match spec {
TermSpec::VerbSplice {
arg_root_idx,
fragment_path,
} => {
let arg_pos = &spec_pos[*arg_root_idx as usize];
stmts.push(quote! {
let __spliced = ::uor_foundation::enforcement::inline_verb_fragment(
buf,
len,
#fragment_path,
(#arg_pos) as u32,
);
buf = __spliced.0;
len = __spliced.1;
let #pos_id: usize = len - 1;
});
}
other => {
let term_expr = render_atomic_term_in_builder(other, &spec_pos);
stmts.push(quote! {
buf[len] = #term_expr;
let #pos_id: usize = len;
len += 1;
});
}
}
}
quote! {
{
const ROUTE_ARENA_CAP: usize = 256;
const fn __build_arena() -> ([::uor_foundation::enforcement::Term; ROUTE_ARENA_CAP], usize) {
let mut buf: [::uor_foundation::enforcement::Term; ROUTE_ARENA_CAP] =
[::uor_foundation::enforcement::Term::Variable { name_index: 0u32 }; ROUTE_ARENA_CAP];
let mut len: usize = 0;
#( #stmts )*
(buf, len)
}
const ROUTE_BUILT: ([::uor_foundation::enforcement::Term; ROUTE_ARENA_CAP], usize) =
__build_arena();
const ROUTE_LEN: usize = ROUTE_BUILT.1;
match ROUTE_BUILT.0.split_at_checked(ROUTE_LEN) {
Some((head, _)) => head,
None => &[],
}
}
}
}
#[proc_macro]
pub fn prism_model(input: TokenStream) -> TokenStream {
let parsed = parse_macro_input!(input as PrismModelInput);
let PrismModelInput {
model_vis,
model_name,
route_vis,
route_name,
h_ty,
b_ty,
a_ty,
r_ty,
input_ty,
output_ty,
route_input_ident,
route_body,
resolvers_body,
} = parsed;
let (resolver_ty_tokens, resolver_construction) = match (&r_ty, &resolvers_body) {
(Some(r), Some(block)) => (quote! { #r }, quote! { #block }),
(Some(r), None) => (
quote! { #r },
quote! { <#r as ::core::default::Default>::default() },
),
(None, _) => (
quote! { ::uor_foundation::pipeline::NullResolverTuple },
quote! { ::uor_foundation::pipeline::NullResolverTuple },
),
};
let mut arena: Vec<TermSpec> = Vec::new();
let mut scope = BindingScope {
route_input_ty: Some(input_ty.clone()),
..BindingScope::default()
};
if let Err(e) = emit_term_for_block(&route_body, &route_input_ident, &mut arena, &mut scope) {
return e.to_compile_error().into();
}
let route_has_verb_splices = arena
.iter()
.any(|s| matches!(s, TermSpec::VerbSplice { .. }));
let route_arena_expr = if route_has_verb_splices {
render_const_fn_arena_builder(&arena)
} else {
let term_specs = render_arena(&arena);
quote! { &[ #( #term_specs ),* ] }
};
let route_terms_const = Ident::new(
&format!(
"ROUTE_TERMS_FOR_{}",
to_screaming_snake(&model_name.to_string())
),
model_name.span(),
);
let expansion = quote! {
#model_vis struct #model_name;
#route_vis struct #route_name;
#[allow(non_upper_case_globals, dead_code)]
const #route_terms_const: &[::uor_foundation::enforcement::Term] =
#route_arena_expr;
impl ::uor_foundation::pipeline::__sdk_seal::Sealed for #model_name {}
impl ::uor_foundation::pipeline::__sdk_seal::Sealed for #route_name {}
impl ::uor_foundation::pipeline::FoundationClosed for #route_name {
fn arena_slice() -> &'static [::uor_foundation::enforcement::Term] {
#route_terms_const
}
}
impl ::uor_foundation::pipeline::PrismModel<#h_ty, #b_ty, #a_ty, #resolver_ty_tokens> for #model_name {
type Input = #input_ty;
type Output = #output_ty;
type Route = #route_name;
fn forward(
input: <Self as ::uor_foundation::pipeline::PrismModel<#h_ty, #b_ty, #a_ty, #resolver_ty_tokens>>::Input,
) -> ::core::result::Result<
::uor_foundation::enforcement::Grounded<
<Self as ::uor_foundation::pipeline::PrismModel<#h_ty, #b_ty, #a_ty, #resolver_ty_tokens>>::Output,
>,
::uor_foundation::PipelineFailure,
> {
let __resolvers: #resolver_ty_tokens = #resolver_construction;
::uor_foundation::pipeline::run_route::<
#h_ty,
#b_ty,
#a_ty,
Self,
#resolver_ty_tokens,
>(input, &__resolvers)
}
}
};
expansion.into()
}
struct OutputShapeInput {
struct_vis: syn::Visibility,
struct_name: Ident,
impl_iri: syn::LitStr,
impl_site_count: syn::Expr,
impl_constraints: syn::Expr,
impl_cycle_size: Option<syn::Expr>,
}
impl Parse for OutputShapeInput {
fn parse(input: ParseStream) -> Result<Self> {
let struct_vis: syn::Visibility = input.parse()?;
input.parse::<Token![struct]>()?;
let struct_name: Ident = input.parse()?;
input.parse::<Token![;]>()?;
input.parse::<Token![impl]>()?;
let trait_ident: Ident = input.parse()?;
if trait_ident != "ConstrainedTypeShape" {
return Err(syn::Error::new(
trait_ident.span(),
"output_shape! expects `impl ConstrainedTypeShape for <Name>`",
));
}
input.parse::<Token![for]>()?;
let target: Ident = input.parse()?;
if target != struct_name {
return Err(syn::Error::new(
target.span(),
"output_shape!'s `impl ConstrainedTypeShape for <Name>` target must match the declared struct",
));
}
let body;
syn::braced!(body in input);
body.parse::<Token![const]>()?;
let kw_iri: Ident = body.parse()?;
if kw_iri != "IRI" {
return Err(syn::Error::new(
kw_iri.span(),
"expected `const IRI: &'static str = ...`",
));
}
body.parse::<Token![:]>()?;
let _ty: syn::Type = body.parse()?;
body.parse::<Token![=]>()?;
let impl_iri: syn::LitStr = body.parse()?;
body.parse::<Token![;]>()?;
body.parse::<Token![const]>()?;
let kw_sc: Ident = body.parse()?;
if kw_sc != "SITE_COUNT" {
return Err(syn::Error::new(
kw_sc.span(),
"expected `const SITE_COUNT: usize = ...`",
));
}
body.parse::<Token![:]>()?;
let _ty: syn::Type = body.parse()?;
body.parse::<Token![=]>()?;
let impl_site_count: syn::Expr = body.parse()?;
body.parse::<Token![;]>()?;
body.parse::<Token![const]>()?;
let kw_cn: Ident = body.parse()?;
if kw_cn != "CONSTRAINTS" {
return Err(syn::Error::new(
kw_cn.span(),
"expected `const CONSTRAINTS: &'static [ConstraintRef] = ...`",
));
}
body.parse::<Token![:]>()?;
let _ty: syn::Type = body.parse()?;
body.parse::<Token![=]>()?;
let impl_constraints: syn::Expr = body.parse()?;
body.parse::<Token![;]>()?;
let impl_cycle_size: Option<syn::Expr> = if body.peek(Token![const]) {
body.parse::<Token![const]>()?;
let kw_cs: Ident = body.parse()?;
if kw_cs != "CYCLE_SIZE" {
return Err(syn::Error::new(
kw_cs.span(),
"expected `const CYCLE_SIZE: u64 = ...` (the only optional const recognised by output_shape! is CYCLE_SIZE per ADR-032)",
));
}
body.parse::<Token![:]>()?;
let _ty: syn::Type = body.parse()?;
body.parse::<Token![=]>()?;
let expr: syn::Expr = body.parse()?;
body.parse::<Token![;]>()?;
Some(expr)
} else {
None
};
Ok(Self {
struct_vis,
struct_name,
impl_iri,
impl_site_count,
impl_constraints,
impl_cycle_size,
})
}
}
#[proc_macro]
pub fn output_shape(input: TokenStream) -> TokenStream {
let parsed = parse_macro_input!(input as OutputShapeInput);
let OutputShapeInput {
struct_vis,
struct_name,
impl_iri,
impl_site_count,
impl_constraints,
impl_cycle_size,
} = parsed;
let cycle_size_tokens = match impl_cycle_size {
Some(expr) => quote! { #expr },
None => quote! {
::uor_foundation::pipeline::cycle_size_power(256, #impl_site_count)
},
};
let expansion = quote! {
#struct_vis struct #struct_name;
impl ::uor_foundation::pipeline::ConstrainedTypeShape for #struct_name {
const IRI: &'static str = #impl_iri;
const SITE_COUNT: usize = #impl_site_count;
const CONSTRAINTS: &'static [::uor_foundation::pipeline::ConstraintRef] =
#impl_constraints;
const CYCLE_SIZE: u64 = #cycle_size_tokens;
}
impl ::uor_foundation::pipeline::__sdk_seal::Sealed for #struct_name {}
impl ::uor_foundation::enforcement::GroundedShape for #struct_name {}
impl ::uor_foundation::pipeline::IntoBindingValue for #struct_name {
const MAX_BYTES: usize =
<#struct_name as ::uor_foundation::pipeline::ConstrainedTypeShape>::SITE_COUNT;
fn into_binding_bytes(
&self,
_out: &mut [u8],
) -> ::core::result::Result<usize, ::uor_foundation::enforcement::ShapeViolation> {
Ok(0)
}
}
};
expansion.into()
}
struct VerbInput {
fn_vis: syn::Visibility,
fn_name: Ident,
input_param: Ident,
input_ty: syn::Type,
output_ty: syn::Type,
body: syn::Block,
}
impl Parse for VerbInput {
fn parse(input: ParseStream) -> Result<Self> {
let fn_vis: syn::Visibility = input.parse()?;
input.parse::<Token![fn]>()?;
let fn_name: Ident = input.parse()?;
let params;
syn::parenthesized!(params in input);
let input_param: Ident = params.parse()?;
params.parse::<Token![:]>()?;
let input_ty: syn::Type = params.parse()?;
input.parse::<Token![->]>()?;
let output_ty: syn::Type = input.parse()?;
let body: syn::Block = input.parse()?;
Ok(Self {
fn_vis,
fn_name,
input_param,
input_ty,
output_ty,
body,
})
}
}
#[proc_macro]
pub fn verb(input: TokenStream) -> TokenStream {
let parsed = parse_macro_input!(input as VerbInput);
let VerbInput {
fn_vis,
fn_name,
input_param,
input_ty,
output_ty,
body,
} = parsed;
let mut arena: Vec<TermSpec> = Vec::new();
let mut scope = BindingScope {
route_input_ty: Some(input_ty.clone()),
..BindingScope::default()
};
if let Err(e) = emit_term_for_block(&body, &input_param, &mut arena, &mut scope) {
return e.to_compile_error().into();
}
let self_const_name = format!("VERB_TERMS_{}", to_screaming_snake(&fn_name.to_string()));
for spec in &arena {
if let TermSpec::VerbSplice { fragment_path, .. } = spec {
if fragment_path.to_string().trim() == self_const_name {
return syn::Error::new(
fn_name.span(),
format!(
"verb-closure violation (ADR-024): `{}`'s body references itself directly; the verb-reference graph through non-`recurse` operators must be acyclic. Lift the recursion through `recurse(...)` (G7) instead.",
fn_name
),
)
.to_compile_error()
.into();
}
}
}
let body_has_verb_splices = arena
.iter()
.any(|s| matches!(s, TermSpec::VerbSplice { .. }));
let verb_fragment_expr = if body_has_verb_splices {
render_const_fn_arena_builder(&arena)
} else {
let term_specs = render_arena(&arena);
quote! { &[ #( #term_specs ),* ] }
};
let const_name = Ident::new(
&format!("VERB_TERMS_{}", to_screaming_snake(&fn_name.to_string())),
fn_name.span(),
);
let accessor_name = Ident::new(&format!("{}_term_arena", fn_name), fn_name.span());
let expansion = quote! {
#[allow(non_upper_case_globals, dead_code)]
#fn_vis const #const_name: &[::uor_foundation::enforcement::Term] =
#verb_fragment_expr;
#fn_vis fn #accessor_name() -> &'static [::uor_foundation::enforcement::Term] {
#const_name
}
#[allow(unused_variables, unreachable_code)]
#fn_vis fn #fn_name(#input_param: #input_ty) -> #output_ty {
let _ = #input_param;
unimplemented!(
"verb `{}` body is catamorphism-evaluated by foundation's pipeline; \
callers reach it through the term-tree accessor `{}_term_arena()`, \
not by direct Rust invocation",
stringify!(#fn_name),
stringify!(#fn_name),
)
}
};
expansion.into()
}
struct UseVerbsInput {
crate_path: syn::Path,
verb_names: Vec<Ident>,
}
impl Parse for UseVerbsInput {
fn parse(input: ParseStream) -> Result<Self> {
let from_kw: Ident = input.parse()?;
if from_kw != "from" {
return Err(syn::Error::new(
from_kw.span(),
"expected `from <crate_path>`",
));
}
let crate_path: syn::Path = input.parse()?;
let body;
syn::braced!(body in input);
let mut verb_names: Vec<Ident> = Vec::new();
while !body.is_empty() {
verb_names.push(body.parse()?);
if body.peek(Token![,]) {
body.parse::<Token![,]>()?;
}
}
let _ = input.parse::<Token![;]>();
Ok(Self {
crate_path,
verb_names,
})
}
}
#[proc_macro]
pub fn use_verbs(input: TokenStream) -> TokenStream {
let parsed = parse_macro_input!(input as UseVerbsInput);
let UseVerbsInput {
crate_path,
verb_names,
} = parsed;
let mut imports: Vec<proc_macro2::TokenStream> = Vec::with_capacity(verb_names.len() * 3);
for name in &verb_names {
let arena_name = Ident::new(&format!("{}_term_arena", name), name.span());
let const_name = Ident::new(
&format!("VERB_TERMS_{}", to_screaming_snake(&name.to_string())),
name.span(),
);
imports.push(quote! { pub use #crate_path::#name; });
imports.push(quote! { pub use #crate_path::#arena_name; });
imports.push(quote! { pub use #crate_path::#const_name; });
}
let expansion = quote! {
#( #imports )*
};
expansion.into()
}
fn lexically_earlier(a: &Ident, b: &Ident) -> String {
let a_s = a.to_string();
let b_s = b.to_string();
if a_s.as_str() <= b_s.as_str() {
a_s
} else {
b_s
}
}
fn lexically_later(a: &Ident, b: &Ident) -> String {
let a_s = a.to_string();
let b_s = b.to_string();
if a_s.as_str() > b_s.as_str() {
a_s
} else {
b_s
}
}
fn canonical_operand_pair(a: &Ident, b: &Ident) -> (Ident, Ident) {
let a_s = a.to_string();
let b_s = b.to_string();
if a_s.as_str() <= b_s.as_str() {
(a.clone(), b.clone())
} else {
(b.clone(), a.clone())
}
}
fn format_ident_suffix(base: &Ident, suffix: &str) -> Ident {
let upper_base = to_screaming_snake(&base.to_string());
let joined = format!("{upper_base}{suffix}");
Ident::new(&joined, base.span())
}
fn to_screaming_snake(s: &str) -> String {
let mut out = String::with_capacity(s.len() + 4);
let chars: Vec<char> = s.chars().collect();
for (i, ch) in chars.iter().enumerate() {
if *ch == '_' {
if !out.ends_with('_') && !out.is_empty() {
out.push('_');
}
continue;
}
if ch.is_ascii_uppercase() {
let prev_lower_or_digit = i > 0
&& chars[i - 1] != '_'
&& (chars[i - 1].is_ascii_lowercase() || chars[i - 1].is_ascii_digit());
let run_ending = i > 0
&& chars[i - 1].is_ascii_uppercase()
&& i + 1 < chars.len()
&& chars[i + 1].is_ascii_lowercase();
if (prev_lower_or_digit || run_ending) && !out.ends_with('_') && !out.is_empty() {
out.push('_');
}
out.push(*ch);
} else {
out.push(ch.to_ascii_uppercase());
}
}
out
}
struct AxisInput {
trait_decl: syn::ItemTrait,
}
impl Parse for AxisInput {
fn parse(input: ParseStream) -> Result<Self> {
let trait_decl: syn::ItemTrait = input.parse()?;
Ok(Self { trait_decl })
}
}
#[proc_macro]
pub fn axis(input: TokenStream) -> TokenStream {
let parsed = parse_macro_input!(input as AxisInput);
let trait_decl = parsed.trait_decl;
let trait_name = trait_decl.ident.clone();
let mut kernel_idents: Vec<Ident> = Vec::new();
for item in &trait_decl.items {
if let syn::TraitItem::Fn(fn_item) = item {
kernel_idents.push(fn_item.sig.ident.clone());
}
}
let mut kernel_consts: Vec<proc_macro2::TokenStream> = Vec::new();
for (i, ident) in kernel_idents.iter().enumerate() {
let upper_name = ident.to_string().to_ascii_uppercase();
let const_name = Ident::new(&format!("KERNEL_{upper_name}"), ident.span());
let id = i as u32;
kernel_consts.push(quote! {
pub const #const_name: u32 = #id;
});
}
let dispatch_arms: Vec<proc_macro2::TokenStream> = kernel_idents
.iter()
.enumerate()
.map(|(i, ident)| {
let id = i as u32;
quote! {
#id => <T as #trait_name>::#ident(input, out),
}
})
.collect();
let expansion = quote! {
#trait_decl
#(#kernel_consts)*
impl<T: #trait_name> ::uor_foundation::pipeline::AxisExtension for T {
const AXIS_ADDRESS: &'static str = <T as #trait_name>::AXIS_ADDRESS;
const MAX_OUTPUT_BYTES: usize = <T as #trait_name>::MAX_OUTPUT_BYTES;
fn dispatch_kernel(
kernel_id: u32,
input: &[u8],
out: &mut [u8],
) -> ::core::result::Result<usize, ::uor_foundation::enforcement::ShapeViolation> {
match kernel_id {
#(#dispatch_arms)*
_ => Err(::uor_foundation::enforcement::ShapeViolation {
shape_iri: "https://uor.foundation/axis/AxisExtensionShape",
constraint_iri: "https://uor.foundation/axis/AxisExtensionShape/kernelId",
property_iri: "https://uor.foundation/axis/kernelId",
expected_range: "https://uor.foundation/axis/RecognisedKernelId",
min_count: 0,
max_count: 0,
kind: ::uor_foundation::ViolationKind::ValueCheck,
}),
}
}
}
};
expansion.into()
}
struct ResolverInput {
struct_vis: syn::Visibility,
struct_name: Ident,
hasher_param: Ident,
fields: Vec<(Ident, syn::Type)>,
}
impl Parse for ResolverInput {
fn parse(input: ParseStream) -> Result<Self> {
let struct_vis: syn::Visibility = input.parse()?;
input.parse::<Token![struct]>()?;
let struct_name: Ident = input.parse()?;
input.parse::<Token![<]>()?;
let hasher_param: Ident = input.parse()?;
if input.peek(Token![:]) {
input.parse::<Token![:]>()?;
let _: syn::Type = input.parse()?;
}
input.parse::<Token![>]>()?;
let body;
syn::braced!(body in input);
let mut fields: Vec<(Ident, syn::Type)> = Vec::new();
while !body.is_empty() {
if body.peek(Token![pub]) {
let _: syn::Visibility = body.parse()?;
}
let field_name: Ident = body.parse()?;
body.parse::<Token![:]>()?;
let field_ty: syn::Type = body.parse()?;
fields.push((field_name, field_ty));
if body.peek(Token![,]) {
body.parse::<Token![,]>()?;
}
}
Ok(Self {
struct_vis,
struct_name,
hasher_param,
fields,
})
}
}
const RESOLVER_FIELD_TABLE: &[(&str, &str, &str, &str, &str)] = &[
(
"nerve",
"Nerve",
"NerveResolver",
"HasNerveResolver",
"nerve_resolver",
),
(
"chain_complex",
"ChainComplex",
"ChainComplexResolver",
"HasChainComplexResolver",
"chain_complex_resolver",
),
(
"homology_groups",
"HomologyGroup",
"HomologyGroupResolver",
"HasHomologyGroupResolver",
"homology_group_resolver",
),
(
"cochain_complex",
"CochainComplex",
"CochainComplexResolver",
"HasCochainComplexResolver",
"cochain_complex_resolver",
),
(
"cohomology_groups",
"CohomologyGroup",
"CohomologyGroupResolver",
"HasCohomologyGroupResolver",
"cohomology_group_resolver",
),
(
"postnikov",
"Postnikov",
"PostnikovResolver",
"HasPostnikovResolver",
"postnikov_resolver",
),
(
"homotopy_groups",
"HomotopyGroup",
"HomotopyGroupResolver",
"HasHomotopyGroupResolver",
"homotopy_group_resolver",
),
(
"k_invariants",
"KInvariant",
"KInvariantResolver",
"HasKInvariantResolver",
"k_invariant_resolver",
),
];
#[proc_macro]
pub fn resolver(input: TokenStream) -> TokenStream {
let parsed = parse_macro_input!(input as ResolverInput);
let ResolverInput {
struct_vis,
struct_name,
hasher_param,
fields,
} = parsed;
let recognised: Vec<&str> = RESOLVER_FIELD_TABLE.iter().map(|t| t.0).collect();
for (name, _) in &fields {
let name_str = name.to_string();
if !recognised.contains(&name_str.as_str()) {
let recognised_csv = recognised.join(", ");
return syn::Error::new_spanned(
name,
format!(
"closure violation: `resolver!` field `{name_str}` is not a recognised resolver category. Recognised: {recognised_csv}"
),
)
.to_compile_error()
.into();
}
}
let struct_fields: Vec<proc_macro2::TokenStream> = fields
.iter()
.map(|(name, ty)| quote::quote! { pub #name: #ty, })
.collect();
let category_idents: Vec<proc_macro2::TokenStream> = fields
.iter()
.map(|(name, _)| {
let entry = RESOLVER_FIELD_TABLE
.iter()
.find(|t| t.0 == name.to_string().as_str())
.unwrap_or(&RESOLVER_FIELD_TABLE[0]);
let cat = syn::Ident::new(entry.1, name.span());
quote::quote! { ::uor_foundation::pipeline::ResolverCategory::#cat }
})
.collect();
let arity = fields.len();
let has_impls: Vec<proc_macro2::TokenStream> = RESOLVER_FIELD_TABLE
.iter()
.map(|entry| {
let cat_field = entry.0;
let resolver_trait = syn::Ident::new(entry.2, struct_name.span());
let marker = syn::Ident::new(entry.3, struct_name.span());
let accessor = syn::Ident::new(entry.4, struct_name.span());
if let Some((field_name, field_ty)) = fields
.iter()
.find(|(n, _)| *n == cat_field)
{
quote::quote! {
impl<#hasher_param: ::uor_foundation::enforcement::Hasher>
::uor_foundation::pipeline::#marker<#hasher_param>
for #struct_name<#hasher_param>
where
#field_ty: ::uor_foundation::pipeline::#resolver_trait<#hasher_param>,
{
fn #accessor(&self) -> &dyn ::uor_foundation::pipeline::#resolver_trait<#hasher_param> {
&self.#field_name
}
}
}
} else {
quote::quote! {
impl<#hasher_param: ::uor_foundation::enforcement::Hasher>
::uor_foundation::pipeline::#marker<#hasher_param>
for #struct_name<#hasher_param>
{
fn #accessor(&self) -> &dyn ::uor_foundation::pipeline::#resolver_trait<#hasher_param> {
&::uor_foundation::pipeline::NullResolverTuple
}
}
}
}
})
.collect();
let default_field_inits: Vec<proc_macro2::TokenStream> = fields
.iter()
.map(|(name, _)| quote::quote! { #name: ::core::default::Default::default(), })
.collect();
let default_where_clauses: Vec<proc_macro2::TokenStream> = fields
.iter()
.map(|(_, ty)| quote::quote! { #ty: ::core::default::Default, })
.collect();
let expansion = quote::quote! {
#struct_vis struct #struct_name<#hasher_param: ::uor_foundation::enforcement::Hasher> {
#(#struct_fields)*
#[doc(hidden)]
pub _phantom: ::core::marker::PhantomData<#hasher_param>,
}
impl<#hasher_param: ::uor_foundation::enforcement::Hasher>
::uor_foundation::pipeline::__sdk_seal::Sealed
for #struct_name<#hasher_param> {}
impl<#hasher_param: ::uor_foundation::enforcement::Hasher>
::uor_foundation::pipeline::ResolverTuple
for #struct_name<#hasher_param>
{
const ARITY: usize = #arity;
const CATEGORIES: &'static [::uor_foundation::pipeline::ResolverCategory] =
&[#(#category_idents),*];
}
impl<#hasher_param: ::uor_foundation::enforcement::Hasher>
::core::default::Default
for #struct_name<#hasher_param>
where
#(#default_where_clauses)*
{
fn default() -> Self {
Self {
#(#default_field_inits)*
_phantom: ::core::marker::PhantomData,
}
}
}
#(#has_impls)*
};
expansion.into()
}