From 7516517078d71183cfa4c409aa8a6948675ebe7a Mon Sep 17 00:00:00 2001 From: unspeaker Date: Wed, 21 May 2025 13:57:03 +0300 Subject: [PATCH] proc, view: fix usage of builtins --- output/src/ops.rs | 5 +- output/src/ops/either.rs | 38 ++++++++++++++ output/src/ops/{cond.rs => when.rs} | 37 ------------- proc/src/proc_view.rs | 81 +++++++++++++++-------------- 4 files changed, 83 insertions(+), 78 deletions(-) create mode 100644 output/src/ops/either.rs rename output/src/ops/{cond.rs => when.rs} (51%) diff --git a/output/src/ops.rs b/output/src/ops.rs index 7eba655..7b8010e 100644 --- a/output/src/ops.rs +++ b/output/src/ops.rs @@ -1,9 +1,10 @@ +//mod reduce; pub use self::reduce::*; mod align; pub use self::align::*; mod bsp; pub use self::bsp::*; -mod cond; pub use self::cond::*; +mod either; pub use self::either::*; mod map; pub use self::map::*; mod memo; pub use self::memo::*; mod stack; pub use self::stack::*; -//mod reduce; pub use self::reduce::*; mod thunk; pub use self::thunk::*; mod transform; pub use self::transform::*; +mod when; pub use self::when::*; diff --git a/output/src/ops/either.rs b/output/src/ops/either.rs new file mode 100644 index 0000000..75c6ccf --- /dev/null +++ b/output/src/ops/either.rs @@ -0,0 +1,38 @@ +use crate::*; + +/// Show one item if a condition is true and another if the condition is false +pub struct Either(pub bool, pub A, pub B); +impl Either { + /// Create a ternary condition. + pub const fn new (c: bool, a: A, b: B) -> Self { + Self(c, a, b) + } +} +#[cfg(feature = "dsl")] +impl + Dsl + Dsl> Namespace for Either { + fn take_from <'source> ( + state: &T, + token: &mut TokenIter<'source> + ) -> Perhaps { + if let Some(Token { value: Value::Key("either"), .. }) = token.peek() { + let base = token.clone(); + let _ = token.next().unwrap(); + return Ok(Some(Self( + state.take_or_fail(token, "either: no condition")?, + state.take_or_fail(token, "either: no content 1")?, + state.take_or_fail(token, "either: no content 2")?, + ))) + } + Ok(None) + } +} +impl, B: Render> Content for Either { + fn layout (&self, to: E::Area) -> E::Area { + let Self(cond, a, b) = self; + if *cond { a.layout(to) } else { b.layout(to) } + } + fn render (&self, to: &mut E) { + let Self(cond, a, b) = self; + if *cond { a.render(to) } else { b.render(to) } + } +} diff --git a/output/src/ops/cond.rs b/output/src/ops/when.rs similarity index 51% rename from output/src/ops/cond.rs rename to output/src/ops/when.rs index be950fe..188a5b5 100644 --- a/output/src/ops/cond.rs +++ b/output/src/ops/when.rs @@ -8,15 +8,6 @@ impl When { Self(c, a) } } - -/// Show one item if a condition is true and another if the condition is false -pub struct Either(pub bool, pub A, pub B); -impl Either { - /// Create a ternary condition. - pub const fn new (c: bool, a: A, b: B) -> Self { - Self(c, a, b) - } -} #[cfg(feature = "dsl")] impl + Dsl> Namespace for When { fn take_from <'source> ( @@ -55,31 +46,3 @@ impl> Content for When { if *cond { item.render(to) } } } -#[cfg(feature = "dsl")] -impl + Dsl + Dsl> Namespace for Either { - fn take_from <'source> ( - state: &T, - token: &mut TokenIter<'source> - ) -> Perhaps { - if let Some(Token { value: Value::Key("either"), .. }) = token.peek() { - let base = token.clone(); - let _ = token.next().unwrap(); - return Ok(Some(Self( - state.take_or_fail(token, "either: no condition")?, - state.take_or_fail(token, "either: no content 1")?, - state.take_or_fail(token, "either: no content 2")?, - ))) - } - Ok(None) - } -} -impl, B: Render> Content for Either { - fn layout (&self, to: E::Area) -> E::Area { - let Self(cond, a, b) = self; - if *cond { a.layout(to) } else { b.layout(to) } - } - fn render (&self, to: &mut E) { - let Self(cond, a, b) = self; - if *cond { a.render(to) } else { b.render(to) } - } -} diff --git a/proc/src/proc_view.rs b/proc/src/proc_view.rs index 7223dc0..ac18a80 100644 --- a/proc/src/proc_view.rs +++ b/proc/src/proc_view.rs @@ -42,50 +42,53 @@ impl Parse for ViewImpl { impl ToTokens for ViewDef { fn to_tokens (&self, out: &mut TokenStream2) { let Self(ViewMeta { output }, ViewImpl { block, exposed }) = self; - let view = &block.self_ty; - let builtins = builtins().iter().map(|ty|write_quote(quote! { - if let Some(value) = Namespace::<#ty>::take_from(state, &mut exp.clone())? { - return Ok(Some(value.boxed())) - } - })).collect::>(); - let mut available = vec![]; - let exposed: Vec<_> = exposed.iter().map(|(key, value)|{ - available.push(key.clone()); - write_quote(quote! { #key => Some(#view::#value(state).boxed()), }) - }).collect(); - let available: String = available.join(", "); - let error_msg = LitStr::new( - &format!("expected Sym(content), got: {{token:?}}, available: {available}"), - Span::call_site() - ); + let self_ty = &block.self_ty; + let builtins: Vec<_> = builtins_with_types() + .iter() + .map(|ty|write_quote(quote! { + let value: Option<#ty> = Dsl::take(state, &mut exp.clone())?; + if let Some(value) = value { + return Ok(Some(value.boxed())) + } + })) + .collect(); + let exposed: Vec<_> = exposed + .iter() + .map(|(key, value)|write_quote(quote! { + #key => Some(#self_ty::#value(state).boxed()), + })).collect(); write_quote_to(out, quote! { #block /// Generated by [tengri_proc]. /// - /// Delegates the rendering of [#view] to the [#view::view} method, + /// Delegates the rendering of [#self_ty] to the [#self_ty::view} method, /// which you will need to implement, e.g. passing a [TokenIter] /// containing a layout and keybindings config from user dirs. - impl ::tengri::output::Content<#output> for #view { + impl ::tengri::output::Content<#output> for #self_ty { fn content (&self) -> impl Render<#output> { - self.view() + #self_ty::view(self) } } /// Generated by [tengri_proc]. /// - /// Gives [#view] the ability to construct the [Render]able + /// Gives [#self_ty] the ability to construct the [Render]able /// which might corresponds to a given [TokenStream], - /// while taking [#view]'s state into consideration. - impl ::tengri::dsl::Namespace<#view> for Box> { + /// while taking [#self_ty]'s state into consideration. + impl ::tengri::dsl::Namespace<#self_ty> for Box + '_> { fn take_from <'source> ( - state: &#view, + state: &#self_ty, words: &mut ::tengri::dsl::TokenIter<'source> ) -> Perhaps { Ok(if let Some(::tengri::dsl::Token { value, .. }) = words.peek() { match value { + // Expressions are handled by built-in functions + // that operate over constants and symbols. ::tengri::dsl::Value::Exp(_, exp) => { - //#(#builtins)* + #(#builtins)* None }, + // Symbols are handled by user-provided functions + // that take no parameters but `&self`. ::tengri::dsl::Value::Sym(sym) => match sym { #(#exposed)* _ => None @@ -101,21 +104,21 @@ impl ToTokens for ViewDef { } } -fn builtins () -> [TokenStream2;14] { +fn builtins_with_types () -> [TokenStream2;14] { [ - quote! { When::<_> }, - quote! { Either::<_, _> }, - quote! { Align::<_> }, - quote! { Bsp::<_, _> }, - quote! { Fill::<_> }, - quote! { Fixed::<_, _> }, - quote! { Min::<_, _> }, - quote! { Max::<_, _> }, - quote! { Shrink::<_, _> }, - quote! { Expand::<_, _> }, - quote! { Push::<_, _> }, - quote! { Pull::<_, _> }, - quote! { Margin::<_, _> }, - quote! { Padding::<_, _> }, + quote! { When< Box + '_> > }, + quote! { Either< Box + '_>, Box + '_>> }, + quote! { Align< Box + '_> > }, + quote! { Bsp< Box + '_>, Box + '_>> }, + quote! { Fill< Box + '_> > }, + quote! { Fixed<_, Box + '_> > }, + quote! { Min<_, Box + '_> > }, + quote! { Max<_, Box + '_> > }, + quote! { Shrink<_, Box + '_> > }, + quote! { Expand<_, Box + '_> > }, + quote! { Push<_, Box + '_> > }, + quote! { Pull<_, Box + '_> > }, + quote! { Margin<_, Box + '_> > }, + quote! { Padding<_, Box + '_> > }, ] }