#![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 => &[],
}
};
}
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_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 => &[],
}
};
}
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 => &[],
}
};
}
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 #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>,
}
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();
operands.push(input.parse()?);
while input.peek(Token![,]) {
input.parse::<Token![,]>()?;
if input.is_empty() {
break;
}
operands.push(input.parse()?);
}
if operands.len() < 2 {
return Err(syn::Error::new(
name.span(),
"partition_product!/partition_coproduct! require at least two operands",
));
}
Ok(Self { name, operands })
}
}
#[proc_macro]
pub fn partition_product(input: TokenStream) -> TokenStream {
let parsed = parse_macro_input!(input as VariadicShapeArgs);
expand_partition_product(parsed.name, &parsed.operands)
}
fn expand_partition_product(name: Ident, operands: &[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 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 => &[],
}
};
}
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 {}
};
expansion.into()
} else {
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_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 => &[],
}
};
}
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 {}
}
}
#[proc_macro]
pub fn partition_coproduct(input: TokenStream) -> TokenStream {
let parsed = parse_macro_input!(input as VariadicShapeArgs);
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 => &[],
}
};
}
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,
input_ty: syn::Type,
output_ty: syn::Type,
route_input_ident: Ident,
route_body: 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> 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()?;
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()?;
Ok(Self {
model_vis,
model_name,
route_vis,
route_name,
h_ty,
b_ty,
a_ty,
input_ty,
output_ty,
route_input_ident,
route_body,
})
}
}
enum TermSpec {
Literal(u64),
Variable,
Application {
operator: proc_macro2::TokenStream,
args_start: u32,
args_len: u32,
},
HasherProjection { input_index: u32 },
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,
}
#[derive(Default, Clone)]
struct BindingScope {
bindings: Vec<(Ident, usize)>,
}
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::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),
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`, and macro-vocabulary function calls — PrimitiveOps, hash, lift, project, recurse, unfold, plus implementation verbs)",
)),
}
}
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 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::HasherProjection { input_index } => TermSpec::HasherProjection {
input_index: *input_index,
},
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,
}
}
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" => {
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 recurse_idx_placeholder = arena.len() + 1; step_scope.push(state_ident, recurse_idx_placeholder);
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.len() != 1 {
return Err(syn::Error::new_spanned(
step_closure,
"closure violation: `recurse`'s step closure expects exactly 1 parameter (the recursive-call placeholder, G7)",
));
}
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 mut step_scope = scope.clone();
step_scope.shadow_check(&self_ident)?;
let recurse_idx_placeholder = arena.len() + 1; step_scope.push(self_ident, recurse_idx_placeholder);
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)?;
step_scope.push(state_ident, seed_root);
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_ty = match &call.args[0] {
syn::Expr::Path(_) => &call.args[0],
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`)",
));
}
};
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 measure_root = arena.len();
arena.push(TermSpec::Literal(256));
let base_root = arena.len();
arena.push(TermSpec::Literal(0));
let mut step_scope = scope.clone();
step_scope.shadow_check(&idx_ident)?;
step_scope.push(idx_ident, measure_root);
let step_root =
emit_term_for_expr(&pred_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 == "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::HasherProjection {
input_index: input_root as u32,
});
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::HasherProjection { input_index } => TermSpec::HasherProjection {
input_index: *input_index,
},
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,
};
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::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::HasherProjection { input_index } => {
let i = *input_index;
quote! {
::uor_foundation::enforcement::Term::HasherProjection {
input_index: #i,
}
}
}
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,
}
},
})
.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::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::HasherProjection { input_index } => {
let i = pos_at(*input_index);
quote! {
::uor_foundation::enforcement::Term::HasherProjection {
input_index: (#i) 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::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,
input_ty,
output_ty,
route_input_ident,
route_body,
} = parsed;
let mut arena: Vec<TermSpec> = Vec::new();
let mut scope = 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> 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>>::Input,
) -> ::core::result::Result<
::uor_foundation::enforcement::Grounded<
<Self as ::uor_foundation::pipeline::PrismModel<#h_ty, #b_ty, #a_ty>>::Output,
>,
::uor_foundation::PipelineFailure,
> {
::uor_foundation::pipeline::run_route::<#h_ty, #b_ty, #a_ty, Self>(input)
}
}
};
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 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![;]>()?;
Ok(Self {
struct_vis,
struct_name,
impl_iri,
impl_site_count,
impl_constraints,
})
}
}
#[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,
} = parsed;
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;
}
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::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
}