use std::str::FromStr;
use proc_macro2::TokenStream;
use quote::{format_ident, quote};
use syn::{parse::Parse, LitInt};
pub struct ConstructivistLimits {
pub max_fields: u8,
}
impl Parse for ConstructivistLimits {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let max_fields = input.parse::<LitInt>()?;
Ok(ConstructivistLimits {
max_fields: max_fields.base10_parse()?,
})
}
}
pub fn implement_constructivism_core(max_size: u8) -> TokenStream {
let extract_field_impls = impl_all_extract_field(max_size);
let add_to_params = impl_all_add_to_params(max_size);
let defined = impl_all_defined(max_size);
let extracts = impl_all_extracts(max_size);
let mixed = impl_all_mixed(max_size);
let as_params = impl_all_as_params(max_size);
let flattern = impl_all_flattern(max_size);
let contains = impl_all_contains(16);
quote! {
#extract_field_impls
#add_to_params
#defined
#extracts
#as_params
#mixed
#flattern
#contains
}
}
pub fn implement_constructivism(max_size: u8) -> TokenStream {
let source = include_str!("core.include");
let source = source
.lines()
.filter(|l| !l.contains("@constructivist-no-expose"))
.collect::<Vec<_>>()
.join("\n");
let Ok(core) = TokenStream::from_str(source.as_str()) else {
return quote! { compile_error! ("Coudn't parse constructivism::core")};
};
quote! {
#core
implement_constructivism_core!(#max_size);
}
}
fn impl_all_extract_field(max_size: u8) -> TokenStream {
let mut out = quote! {};
for size in 1..max_size + 1 {
for idx in 0..size {
let impl_extract = impl_extract_field(idx, size);
out = quote! { #out #impl_extract }
}
}
out
}
fn impl_all_add_to_params(max_size: u8) -> TokenStream {
let mut out = quote! {};
for size in 1..max_size + 1 {
for idx in 0..size {
let impl_add_to_params = impl_add_to_params(idx, size);
out = quote! { #out #impl_add_to_params }
}
}
out
}
fn impl_all_defined(max_size: u8) -> TokenStream {
let mut out = quote! {};
for size in 1..max_size + 1 {
let defined = impl_defined(size);
out = quote! { #out #defined }
}
out
}
fn impl_all_extracts(max_size: u8) -> TokenStream {
let mut out = quote! {};
for size in 1..max_size + 1 {
let extractable = impl_extractable(size);
out = quote! { #out #extractable };
for defined in 0..size + 1 {
let extract = impl_extract(defined, size);
out = quote! { #out #extract };
}
}
out
}
fn impl_all_mixed(max_size: u8) -> TokenStream {
let mut out = quote! {};
for size in 1..max_size + 1 {
for left in 0..size + 1 {
let right = size - left;
let mixed = impl_mixed(left, right);
out = quote! { #out #mixed };
}
}
out
}
fn impl_all_as_params(max_size: u8) -> TokenStream {
let mut out = quote! {};
for size in 0..max_size + 1 {
let mut ts = quote! {};
let mut ds = quote! {};
let mut us = quote! {};
let mut ps = quote! {};
for i in 0..size {
let ti = format_ident!("T{i}");
ts = quote! { #ts #ti, };
ds = quote! { #ds D<#i, #ti>, };
us = quote! { #us U<#i, #ti>, };
ps = quote! { #ps U::<#i, #ti>(::std::marker::PhantomData), }
}
out = quote! { #out
impl<#ts> AsParams for (#ds) {
type Undefined = Params<(#us)>;
type Defined = Params<(#ds)>;
fn as_params() -> Self::Undefined {
Params(( #ps ))
}
}
}
}
out
}
fn impl_all_flattern(max_depth: u8) -> TokenStream {
let mut out = quote! {};
for depth in 1..max_depth + 1 {
let mut ts = quote! {};
let mut cstr = quote! {};
let mut ns = quote! { () };
let mut vs = quote! {};
let mut dcs = quote! { _ };
for i in 0..depth {
let ti = format_ident!("T{i}");
let pi = format_ident!("p{i}");
let tr = format_ident!("T{}", depth - i - 1);
let pr = format_ident!("p{}", depth - i - 1);
cstr = quote! { #cstr #ti: ConstructItem, };
ts = if i < depth - 1 {
quote! { #ts #ti, }
} else {
quote! { #ts #ti }
};
vs = if i < depth - 1 {
quote! { #vs #pi, }
} else {
quote! { #vs #pi }
};
ns = quote! { (#tr, #ns) };
dcs = quote! { (#pr, #dcs) };
}
out = quote! { #out
impl<#cstr> Flattern for #ns {
type Output = (#ts);
fn flattern(self) -> Self::Output {
let #dcs = self;
( #vs )
}
}
}
}
out
}
fn impl_all_contains(max_size: u8) -> TokenStream {
let mut out = quote! {};
for size in 1..max_size + 1 {
let contains = impl_contains(size);
out = quote! { #out #contains }
}
out
}
fn impl_extract_field(idx: u8, size: u8) -> TokenStream {
let ti = format_ident!("T{idx}");
let fi = quote! { F<#idx, #ti> };
let mut gin = quote! {};
let mut gout = quote! {};
for i in 0..size {
let ai = format_ident!("A{i}");
if i == idx {
gin = quote! { #gin #ai: A<#i, #ti>, }
} else {
gin = quote! { #gin #ai,}
}
gout = quote! { #gout #ai, };
}
quote! {
impl<#ti, #gin> ExtractField<#fi, #ti> for Params<(#gout)> {
fn field(&self, _: &Field<#ti>) -> #fi {
F::<#idx, #ti>(PhantomData)
}
}
}
}
fn impl_add_to_params(idx: u8, size: u8) -> TokenStream {
let ti = format_ident!("T{idx}");
let di = quote! { D<#idx, #ti> };
let ui = quote! { U<#idx, #ti> };
let mut gin = quote! {};
let mut pundef = quote! {};
let mut pdef = quote! {};
let mut pout = quote! {};
let mut dcs = quote! {};
let mut vls = quote! {};
for i in 0..size {
if i == idx {
pundef = quote! { #pundef #ui, };
pdef = quote! { #pdef #di, };
pout = quote! { #pout #di, };
dcs = quote! { #dcs _, };
vls = quote! { #vls rhs, };
} else {
let ai = format_ident!("A{i}");
let pi = format_ident!("p{i}");
gin = quote! { #gin #ai, };
pundef = quote! { #pundef #ai, };
pdef = quote! { #pdef #ai, };
pout = quote! { #pout #ai, };
dcs = quote! { #dcs #pi, };
vls = quote! { #vls #pi, };
}
}
quote! {
impl<#ti, #gin> std::ops::Add<#di> for Params<(#pundef)> {
type Output = Params<(#pout)>;
fn add(self, rhs: #di) -> Self::Output {
let (#dcs) = self.0;
Params((#vls))
}
}
impl<#ti, #gin> std::ops::Add<#di> for Params<(#pdef)> {
type Output = ParamConflict<#ti>;
fn add(self, _: #di) -> Self::Output {
ParamConflict::new()
}
}
}
}
fn impl_extractable(size: u8) -> TokenStream {
let mut ein = quote! {};
let mut edef = quote! {};
let mut eout = quote! {};
let mut dcstr = quote! {};
for i in 0..size {
let ti = format_ident!("T{i}");
let pi = format_ident!("p{i}");
ein = quote! { #ein #ti, };
edef = quote! { #edef D<#i, #ti>, };
dcstr = quote! { #dcstr #pi, };
eout = quote! { #eout #pi.0, };
}
quote! {
impl<#ein> Extractable for (#ein) {
type Input = (#edef);
type Output = (#ein);
fn extract(input: Self::Input) -> Self::Output {
let (#dcstr) = input;
(#eout)
}
}
}
}
fn impl_extract(defined: u8, size: u8) -> TokenStream {
let mut ein = quote! {};
let mut pin = quote! {};
let mut pfor = quote! {};
let mut pcstr = quote! {};
let mut trest = quote! {};
let mut pdcstr = quote! {};
let mut pout = quote! {};
let mut pparams = quote! {};
for i in 0..size {
let ti = format_ident!("T{i}");
let pi = format_ident!("p{i}");
if i < defined {
ein = quote! { #ein #ti, };
pout = quote! { #pout #pi, }
} else {
let j = i - defined;
pcstr = quote! { #pcstr #ti: Shift<#j>, };
trest = quote! { #trest #ti::Target, };
pparams = quote! { #pparams #pi.shift(), };
}
pin = quote! { #pin #ti, };
pfor = quote! { #pfor #ti, };
pdcstr = quote! { #pdcstr #pi, };
}
quote! {
impl<#pin E: Extractable<Input = (#ein)>> ExtractParams<#defined, E> for Params<(#pin)>
where #pcstr
{
type Value = E::Output;
type Rest = Params<(#trest)>;
fn extract_params(self) -> (Self::Value, Self::Rest) {
let (#pdcstr) = self.0;
(
E::extract((#pout)),
Params((#pparams))
)
}
}
}
}
fn impl_defined(size: u8) -> TokenStream {
let mut gin = quote! {};
let mut gout = quote! {};
let mut pout = quote! {};
let mut dcstr = quote! {};
let mut vals = quote! {};
for i in 0..size {
let ti = format_ident!("T{i}");
let pi = format_ident!("p{i}");
gin = quote! { #gin #ti: ExtractValue, };
gout = quote! { #gout #ti, };
pout = quote! { #pout D<#i, #ti::Value>, };
dcstr = quote! { #dcstr #pi, };
vals = quote! { #vals D::<#i, _>(#pi.extract_value()), }
}
quote! {
impl<#gin> Params<(#gout)> {
pub fn defined(self) -> Params<(#pout)> {
let (#dcstr) = self.0;
Params((#vals))
}
}
}
}
fn impl_mixed(left: u8, right: u8) -> TokenStream {
let mut ls = quote! {}; let mut rs = quote! {}; let mut dls = quote! {}; let mut drs = quote! {}; let mut lvs = quote! {}; let mut rvs = quote! {}; let mut shift = quote! {}; let mut output = quote! {}; for i in 0..left.max(right) {
let li = format_ident!("L{i}");
let ri = format_ident!("R{i}");
let lv = format_ident!("l{i}");
let rv = format_ident!("r{i}");
if i < left {
ls = quote! { #ls #li, };
dls = quote! { #dls D<#i, #li>, };
lvs = quote! { #lvs #lv, };
}
if i < right {
rs = quote! { #rs #ri, };
drs = quote! { #drs D<#i, #ri>, };
rvs = quote! { #rvs #rv, };
shift = quote! { #shift let #rv = D::<#i, _>(#rv.0); }
}
}
for i in 0..left + right {
let ti = if i < left {
format_ident!("L{i}")
} else {
format_ident!("R{}", i - left)
};
output = quote! { #output D<#i, #ti>, };
}
quote! {
impl<#ls #rs> Mixed<(#drs)> for (#dls) {
type Output = (#output);
fn split(joined: Self::Output) -> (Self, (#drs)) {
let (#lvs #rvs) = joined;
#shift
((#lvs), (#rvs))
}
}
}
}
fn impl_contains(size: u8) -> TokenStream {
let mut out = quote! {};
let mut tfor = quote! { () };
let mut tin = quote! {};
for i in 0..size {
let ti = format_ident!("T{}", size - i - 1);
tfor = quote! { (#ti, #tfor) };
tin = quote! { #ti, #tin };
}
for impl_size in 0..size {
let mut cnt = quote! { () };
for i in 0..impl_size {
let ti = format_ident!("T{}", size - i - 1);
cnt = quote! { (#ti, #cnt) }
}
out = quote! { #out
impl<I, #tin> Contains<I, #cnt> for #tfor { }
}
}
out = quote! { #out
impl<#tin> Contains<Inclusive, #tfor> for #tfor { }
};
out
}