use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
use crate::parser::*;
pub(crate) fn update_to_lazy_ident(name: syn::Ident) -> syn::Ident {
prefix_ident("Lazy", name)
}
pub struct LazyFieldType(ControlType);
impl LazyFieldType {
fn to_token(&self) -> TokenStream2 {
match self.0 {
ControlType::Bool => quote! {murrelet_livecode::lazy::LazyNodeF32}, ControlType::F32 => quote! {murrelet_livecode::lazy::LazyNodeF32},
ControlType::F32_2 => {
quote! {Vec<murrelet_livecode::lazy::LazyNodeF32>}
}
ControlType::F32_3 => {
quote! {Vec<murrelet_livecode::lazy::LazyNodeF32>}
}
ControlType::Color => {
quote! {Vec<murrelet_livecode::lazy::LazyNodeF32>}
}
ControlType::LazyNodeF32 => {
quote! { murrelet_livecode::lazy::LazyNodeF32 }
}
_ => panic!("unitcell doesn't have this one yet"),
}
}
fn for_world_func(
&self,
ident: syn::Ident,
f32min: Option<f32>,
f32max: Option<f32>,
) -> TokenStream2 {
match self.0 {
ControlType::F32_2 => {
quote! { murrelet_livecode::lazy::eval_lazy_vec2(#ident, ctx) }
}
ControlType::F32_3 => {
quote! { murrelet_livecode::lazy::eval_lazy_vec3(#ident, ctx) }
}
ControlType::Color => {
quote! { murrelet_livecode::lazy::eval_lazy_color(#ident, ctx) }
}
ControlType::Bool => quote! {#ident.eval_lazy(ctx)? > 0.0},
_ => {
let f32_out = match (f32min, f32max) {
(None, None) => quote! {#ident.eval_lazy(ctx)},
(None, Some(max)) => quote! {Ok(f32::min(#ident.eval_lazy(ctx)?, #max))},
(Some(min), None) => quote! {Ok(f32::max(#min, #ident.eval_lazy(ctx)?))},
(Some(min), Some(max)) => {
quote! {Ok(f32::min(f32::max(#min, #ident.eval_lazy(ctx)?), #max))}
}
};
quote! {#f32_out}
}
}
}
fn for_world(&self, idents: StructIdents) -> TokenStream2 {
let name = idents.name();
let orig_ty = idents.orig_ty();
match self.0 {
ControlType::F32_2 => {
quote! { #name: glam::vec2(self.#name[0].eval_lazy(ctx)? as f32, self.#name[1].eval_lazy(ctx)? as f32)}
}
ControlType::F32_3 => {
quote! {#name: glam::vec3(self.#name[0].eval_lazy(ctx)? as f32, self.#name[1].eval_lazy(ctx)? as f32, self.#name[2].eval_lazy(ctx)? as f32)}
}
ControlType::Color => {
quote! {#name: murrelet_common::MurreletColor::hsva(self.#name[0].eval_lazy(ctx)? as f32, self.#name[1].eval_lazy(ctx)? as f32, self.#name[2].eval_lazy(ctx)? as f32, self.#name[3].eval_lazy(ctx)? as f32)}
}
ControlType::Bool => quote! {#name: self.#name.eval_lazy(ctx)? > 0.0},
ControlType::LazyNodeF32 => quote! {#name: self.#name.add_more_defs(ctx)? },
_ => {
let f32_out = match (idents.data.f32min, idents.data.f32max) {
(None, None) => quote! {self.#name.eval_lazy(ctx)?},
(None, Some(max)) => quote! {f32::min(self.#name.eval_lazy(ctx)?, #max)},
(Some(min), None) => quote! {f32::max(#min, self.#name.eval_lazy(ctx)?)},
(Some(min), Some(max)) => {
quote! {f32::min(f32::max(#min, self.#name.eval_lazy(ctx)?), #max)}
}
};
quote! {#name: #f32_out as #orig_ty}
}
}
}
fn for_newtype_world(&self, idents: StructIdents) -> TokenStream2 {
let orig_ty = idents.orig_ty();
match self.0 {
ControlType::F32_2 => {
quote! {vec2(self.0[0].eval_lazy(ctx)? as f32, self.0[1].eval_lazy(ctx)? as f32)}
}
ControlType::Color => {
quote! {MurreletColor::hsva(self.0[0].eval_lazy(ctx)? as f32, self.0[1].eval_lazy(ctx)? as f32, self.0[2].eval_lazy(ctx)? as f32, self.0[3].eval_lazy(ctx)? as f32)}
}
ControlType::Bool => quote! {self.0.eval_lazy(ctx)? > 0.0},
_ => {
let f32_out = match (idents.data.f32min, idents.data.f32max) {
(None, None) => quote! {self.0.eval_lazy(ctx)?},
(None, Some(max)) => quote! {f32::min(self.0.eval_lazy(ctx)?, #max)},
(Some(min), None) => quote! {f32::max(#min, self.0.eval_lazy(ctx)?)},
(Some(min), Some(max)) => {
quote! {f32::min(f32::max(#min, self.0.eval_lazy(ctx)?), #max)}
}
};
quote! {#f32_out as #orig_ty}
}
}
}
}
pub(crate) struct FieldTokensLazy {
pub(crate) for_struct: TokenStream2,
pub(crate) for_world: TokenStream2,
}
impl GenFinal for FieldTokensLazy {
fn make_newtype_struct_final(
idents: ParsedFieldIdent,
variants: Vec<FieldTokensLazy>,
) -> TokenStream2 {
let lc_ident = idents.new_ident;
let name = idents.name;
let vis = idents.vis;
let for_struct = variants.iter().map(|x| x.for_struct.clone());
let for_world = variants.iter().map(|x| x.for_world.clone());
quote! {
#[derive(Debug, Clone, Default, murrelet_livecode_derive::LivecodeOnly)]
#vis struct #lc_ident(#(#for_struct,)*);
impl murrelet_livecode::lazy::IsLazy for #lc_ident {
type Target = #name;
fn eval_lazy(&self, ctx: &murrelet_livecode::expr::MixedEvalDefs) -> murrelet_livecode::types::LivecodeResult<#name> {
Ok(#name(#(#for_world,)*))
}
}
}
}
fn make_struct_final(idents: ParsedFieldIdent, variants: Vec<FieldTokensLazy>) -> TokenStream2 {
let lc_ident = idents.new_ident;
let name = idents.name;
let vis = idents.vis;
let for_struct = variants.iter().map(|x| x.for_struct.clone());
let for_world = variants.iter().map(|x| x.for_world.clone());
quote! {
#[derive(Debug, Clone, Default, murrelet_livecode_derive::LivecodeOnly)]
#vis struct #lc_ident {
#(#for_struct,)*
}
impl murrelet_livecode::lazy::IsLazy for #lc_ident {
type Target = #name;
fn eval_lazy(&self, ctx: &murrelet_livecode::expr::MixedEvalDefs) -> murrelet_livecode::types::LivecodeResult<#name> {
Ok(#name {
#(#for_world,)*
})
}
}
}
}
fn make_enum_final(idents: ParsedFieldIdent, variants: Vec<FieldTokensLazy>) -> TokenStream2 {
let new_enum_ident = idents.new_ident;
let name = idents.name;
let vis = idents.vis;
let tags = idents.lazy_enum_tag;
let for_struct = variants.iter().map(|x| x.for_struct.clone());
let for_world = variants.iter().map(|x| x.for_world.clone());
quote! {
#[derive(Debug, Clone, Default, murrelet_livecode_derive::LivecodeOnly)]
#[allow(non_camel_case_types)]
#tags
#vis enum #new_enum_ident {
#[default]
DefaultNoop,
#(#for_struct,)*
}
impl murrelet_livecode::lazy::IsLazy for #new_enum_ident {
type Target = #name;
fn eval_lazy(&self, ctx: &murrelet_livecode::expr::MixedEvalDefs) -> murrelet_livecode::types::LivecodeResult<#name> {
Ok(match self {
#new_enum_ident::DefaultNoop => panic!("fell back to default"), #(#for_world,)*
})
}
}
}
}
fn from_newtype_struct(idents: StructIdents, _parent_idents: syn::Ident) -> FieldTokensLazy {
let ctrl = idents.control_type();
let for_struct = {
let t = LazyFieldType(ctrl).to_token();
quote! {#t}
};
let for_world = LazyFieldType(ctrl).for_newtype_world(idents.clone());
FieldTokensLazy {
for_struct,
for_world,
}
}
fn from_unnamed_enum(idents: EnumIdents) -> FieldTokensLazy {
let variant_ident = idents.variant_ident();
let name = idents.enum_ident();
let new_enum_ident = Self::new_ident(name.clone());
let unnamed = idents.data.fields.fields;
if unnamed.len() != 1 {
panic!("multiple fields not supported")
};
let t = unnamed.first().unwrap().clone().ty;
let parsed_data_type = ident_from_type(&t);
let is_lazy = parsed_data_type.main_how_to.is_lazy();
let for_struct = {
let new_type = if is_lazy {
parsed_data_type.main_type.clone()
} else {
update_to_lazy_ident(parsed_data_type.main_type)
};
quote! { #variant_ident(#new_type) }
};
let for_world = if is_lazy {
quote! { #new_enum_ident::#variant_ident(s) => #name::#variant_ident(s.clone()) }
} else {
quote! { #new_enum_ident::#variant_ident(s) => #name::#variant_ident(s.eval_lazy(ctx)?) }
};
FieldTokensLazy {
for_struct,
for_world,
}
}
fn from_unit_enum(idents: EnumIdents) -> FieldTokensLazy {
let variant_ident = idents.variant_ident();
let name = idents.enum_ident();
let new_enum_ident = Self::new_ident(name.clone());
let for_struct = {
quote! { #variant_ident }
};
let for_world: TokenStream2 = {
quote! { #new_enum_ident::#variant_ident => #name::#variant_ident }
};
FieldTokensLazy {
for_struct,
for_world,
}
}
fn from_noop_struct(idents: StructIdents) -> FieldTokensLazy {
let name = idents.name();
let new_ty = idents.orig_ty();
let back_to_quote = idents.back_to_quote();
let for_struct = {
quote! {#back_to_quote #name: #new_ty}
};
let for_world: TokenStream2 = {
quote! {#name: self.#name.clone()}
};
FieldTokensLazy {
for_struct,
for_world,
}
}
fn from_type_struct(idents: StructIdents) -> FieldTokensLazy {
let name = idents.name();
let back_to_quote = idents.back_to_quote();
let ctrl = idents.control_type();
let for_struct = {
let t = LazyFieldType(ctrl).to_token();
quote! {#back_to_quote #name: #t}
};
let for_world = LazyFieldType(ctrl).for_world(idents.clone());
FieldTokensLazy {
for_struct,
for_world,
}
}
fn from_recurse_struct_vec(idents: StructIdents) -> FieldTokensLazy {
let name = idents.name();
let orig_ty = idents.orig_ty();
let back_to_quote = idents.back_to_quote();
let parsed_type_info = ident_from_type(&orig_ty);
let how_to_control_internal = parsed_type_info.how_to_control_internal();
let wrapper = parsed_type_info.wrapper_type();
let for_struct = {
let internal_type = match how_to_control_internal {
HowToControlThis::WithType(_, c) => LazyFieldType(*c).to_token(),
HowToControlThis::WithRecurse(_, RecursiveControlType::Struct) => {
let target_type = parsed_type_info.internal_type();
let name = Self::new_ident(target_type.clone());
quote! {#name}
}
HowToControlThis::WithNone(_) => {
let target_type = parsed_type_info.internal_type();
let name = Self::new_ident(target_type.clone());
quote! {#name}
}
e => panic!("need vec something {:?}", e),
};
let new_ty = match wrapper {
VecDepth::NotAVec => unreachable!("huh, parsing a not-vec in the vec function"), VecDepth::Vec => quote! {Vec<#internal_type>},
VecDepth::VecVec => todo!(),
};
quote! {#back_to_quote #name: #new_ty}
};
let for_world = {
match how_to_control_internal {
HowToControlThis::WithType(_, c) => {
let x = syn::Ident::new("x", idents.name().span());
let c =
LazyFieldType(*c).for_world_func(x, idents.data.f32min, idents.data.f32max);
quote! {#name: self.#name.iter().map(|x| #c).collect::<Result<Vec<_>, _>>()?}
}
HowToControlThis::WithRecurse(_, RecursiveControlType::Struct) => {
quote! {#name: self.#name.iter().map(|x| x.eval_lazy(ctx)).collect::<Result<Vec<_>, _>>()?}
}
HowToControlThis::WithNone(_) => {
let target_type = parsed_type_info.internal_type();
let name = Self::new_ident(target_type.clone());
quote! {#name: self.#name.clone()}
}
e => panic!("need vec something {:?}", e),
}
};
FieldTokensLazy {
for_struct,
for_world,
}
}
fn from_newtype_recurse_struct_vec(idents: StructIdents) -> Self {
let orig_ty = idents.orig_ty();
let parsed_type_info = ident_from_type(&orig_ty);
let how_to_control_internal = parsed_type_info.how_to_control_internal();
let for_struct = {
let new_ty = match how_to_control_internal {
HowToControlThis::WithType(_, c) => LazyFieldType(*c).to_token(),
HowToControlThis::WithRecurse(_, RecursiveControlType::Struct) => {
let internal_type = parsed_type_info.internal_type();
let name = Self::new_ident(internal_type);
quote! {#name}
}
HowToControlThis::WithNone(_) => {
let internal_type = parsed_type_info.internal_type();
let name = Self::new_ident(internal_type);
quote! {#name}
}
e => panic!("need vec something {:?}", e),
};
quote! {Vec<#new_ty>}
};
let for_world = {
quote! {self.0.iter().map(|x| x.eval_lazy(ctx)).collect::<Result<Vec<_>, _>>()?}
};
FieldTokensLazy {
for_struct,
for_world,
}
}
fn from_recurse_struct_unitcell(idents: StructIdents) -> FieldTokensLazy {
let name = idents.name();
let orig_ty = idents.orig_ty();
let back_to_quote = idents.back_to_quote();
let parsed_type_info = ident_from_type(&orig_ty);
let how_to_control_internal = parsed_type_info.how_to_control_internal();
let for_struct = {
let new_ty = match how_to_control_internal {
HowToControlThis::WithRecurse(_, RecursiveControlType::Struct) => {
let internal_type = parsed_type_info.internal_type();
let name = update_to_lazy_ident(internal_type);
quote! {murrelet_livecode::unitcells::UnitCells<#name>}
}
HowToControlThis::WithRecurse(_, RecursiveControlType::StructLazy) => {
let internal_type = parsed_type_info.internal_type();
let name = internal_type;
quote! {murrelet_livecode::unitcells::UnitCells<#name>}
}
e => panic!("need lazy something {:?}", e),
};
quote! {#back_to_quote #name: #new_ty}
};
let for_world = {
if how_to_control_internal.is_lazy() {
quote! {#name: self.#name.clone() }
} else {
quote! {#name: {
let c = self.#name.iter().map(|x|
x.node.eval_lazy(ctx).map(|r| x.to_other_type(r))
).collect::<Result<Vec<_>, _>>()?;
murrelet_livecode::unitcells::UnitCells::new(c)
}
}
}
};
FieldTokensLazy {
for_struct,
for_world,
}
}
fn from_recurse_struct_struct(idents: StructIdents) -> Self {
let name = idents.name();
let orig_ty = idents.orig_ty();
let back_to_quote = idents.back_to_quote();
let for_struct = {
let new_ty = {
let DataFromType { main_type, .. } = ident_from_type(&orig_ty);
let ref_lc_ident = Self::new_ident(main_type.clone());
quote! {#ref_lc_ident}
};
quote! {#back_to_quote #name: #new_ty}
};
let for_world = {
quote! {#name: self.#name.eval_lazy(ctx)?}
};
FieldTokensLazy {
for_struct,
for_world,
}
}
fn new_ident(name: syn::Ident) -> syn::Ident {
update_to_lazy_ident(name)
}
fn from_recurse_struct_lazy(idents: StructIdents) -> Self {
Self::from_noop_struct(idents)
}
}