diff --git a/dsl/src/dsl.rs b/dsl/src/dsl.rs index f57c8ce..530e4f5 100644 --- a/dsl/src/dsl.rs +++ b/dsl/src/dsl.rs @@ -7,7 +7,6 @@ use const_panic::PanicFmt; use std::fmt::Debug; pub(crate) use ::tengri_core::*; pub(crate) use std::error::Error; -pub(crate) use std::sync::Arc; pub(crate) use konst::string::{str_range, char_indices}; pub(crate) use thiserror::Error; pub(crate) use self::DslError::*; @@ -24,6 +23,10 @@ impl<'s> Dsl for &'s str { fn src (&self) -> &str { self } } +impl Dsl for String { + fn src (&self) -> &str { self.as_str() } +} + impl Dsl for std::sync::Arc { fn src (&self) -> &str { self.as_ref() } } diff --git a/dsl/src/dsl_conv.rs b/dsl/src/dsl_conv.rs index 65f05d6..0600679 100644 --- a/dsl/src/dsl_conv.rs +++ b/dsl/src/dsl_conv.rs @@ -11,12 +11,6 @@ pub trait FromDsl: Sized { } } -impl<'s, T: FromDsl, U> DslInto<'s, T> for U { - fn dsl_into (&self, dsl: &impl Dsl) -> Perhaps { - T::from_dsl(self, dsl) - } -} - /// `self` + [Dsl] -> `T` pub trait DslInto<'s, T> { fn dsl_into (&'s self, dsl: &impl Dsl) -> Perhaps; @@ -28,6 +22,17 @@ pub trait DslInto<'s, T> { } } +/// `self` + `T` -> [Dsl] +pub trait DslFrom { + fn dsl_from (&self, dsl: &T) -> Perhaps; + fn dsl_from_or (&self, dsl: &T, err: Box) -> Usually { + self.dsl_from(dsl)?.ok_or(err) + } + fn dsl_from_or_else (&self, dsl: &T, err: impl Fn()->Box) -> Usually { + self.dsl_from(dsl)?.ok_or_else(err) + } +} + /// `self` + `T` + -> [Dsl] pub trait IntoDsl { fn into_dsl (&self, state: &T) -> Perhaps; @@ -39,13 +44,14 @@ pub trait IntoDsl { } } -/// `self` + `T` -> [Dsl] -pub trait DslFrom { - fn dsl_from (&self, dsl: &impl Dsl) -> Perhaps; - fn dsl_from_or (&self, dsl: &impl Dsl, err: Box) -> Usually { - self.dsl_from(dsl)?.ok_or(err) - } - fn dsl_from_or_else (&self, dsl: &impl Dsl, err: impl Fn()->Box) -> Usually { - self.dsl_from(dsl)?.ok_or_else(err) +impl<'s, T: FromDsl, U> DslInto<'s, T> for U { + fn dsl_into (&self, dsl: &impl Dsl) -> Perhaps { + T::from_dsl(self, dsl) + } +} + +impl, U> IntoDsl for U { + fn into_dsl (&self, state: &T) -> Perhaps { + T::dsl_from(state, self) } } diff --git a/proc/src/proc_view.rs b/proc/src/proc_view.rs index 61cbe6e..bd44c96 100644 --- a/proc/src/proc_view.rs +++ b/proc/src/proc_view.rs @@ -50,10 +50,28 @@ impl ToTokens for ViewDef { impl ViewDef { fn generated (&self) -> impl ToTokens { - let Self(ViewMeta { output }, ViewImpl { block, .. }) = self; - let self_ty = &block.self_ty; - let builtins = self.builtins(); - let exposed = self.exposed(); + let Self(ViewMeta { output }, ViewImpl { block, exposed }) = self; + let self_ty = &block.self_ty; + // Expressions are handled by built-in functions that operate over constants and symbols. + let builtins = builtins_with_boxes_output(quote! { #output }) + .map(|(builtin, builtin_ty)|match builtin { + Single(name) => quote! { + if dsl.exp_head()?.key()? == Some(#name) { + return Ok(Some(#builtin_ty::from_dsl_or_else(self, dsl, + ||format!("failed to load builtin").into())?.boxed())) + } + }, + Prefix(name) => quote! { + if let Some(key) = dsl.exp_head()?.key()? && key.starts_with(#name) { + return Ok(Some(#builtin_ty::from_dsl_or_else(self, dsl, + ||format!("failed to load builtin").into())?.boxed())) + } + }, + }); + // Symbols are handled by user-provided functions that take no parameters but `&self`. + let exposed = exposed.iter().map(|(key, value)|write_quote(quote! { + #key => return Ok(Some(self.#value().boxed())), + })); quote! { /// Generated by [tengri_proc]. /// @@ -66,63 +84,53 @@ impl ViewDef { fn dsl_into (&'state self, dsl: &impl ::tengri::dsl::Dsl) -> Perhaps + 'state>> { - Ok(match dsl.val() { #builtins #exposed _ => return Ok(None) }) + #(#builtins)* + if let Some(sym) = dsl.sym()? { + match sym { + #(#exposed)* + _ => return Err(format!("unknown symbol {sym}").into()) + } + } + Ok(None) } } } } - /// Expressions are handled by built-in functions - /// that operate over constants and symbols. - fn builtins (&self) -> impl ToTokens { - let Self(ViewMeta { output }, ViewImpl { .. }) = self; - let builtins = builtins_with_boxes_output(quote! { #output }).map(|builtin|quote! { - ::tengri::dsl::Val::Exp(_, expr) => return Ok(Some( - #builtin::from_dsl_or_else( - self, - &expr, - || { format!("failed to load builtin").into() } - )?.boxed() - )), - }); - quote! { #(#builtins)* } - } - /// Symbols are handled by user-taked functions that take no parameters but `&self`. - fn exposed (&self) -> impl ToTokens { - let Self(ViewMeta { .. }, ViewImpl { exposed, .. }) = self; - let exposed = exposed.iter().map(|(key, value)|write_quote(quote! { - #key => return Ok(Some(self.#value().boxed())), - })); - quote! { ::tengri::dsl::Val::Sym(key) => match key.as_ref() { #(#exposed)* _ => panic!() } } - } } -fn _builtins_with_holes () -> impl Iterator { +enum Builtin { + Single(TokenStream2), + Prefix(TokenStream2), +} +use Builtin::*; + +fn builtins_with (n: TokenStream2, c: TokenStream2) -> impl Iterator { + [ + (Single(quote!("when")), quote!(When::< #c >)), + (Single(quote!("either")), quote!(Either::< #c, #c>)), + (Prefix(quote!("align/")), quote!(Align::< #c >)), + (Prefix(quote!("bsp/")), quote!(Bsp::< #c, #c>)), + (Prefix(quote!("fill/")), quote!(Fill::< #c >)), + (Prefix(quote!("fixed/")), quote!(Fixed::<#n, #c >)), + (Prefix(quote!("min/")), quote!(Min::<#n, #c >)), + (Prefix(quote!("max/")), quote!(Max::<#n, #c >)), + (Prefix(quote!("shrink/")), quote!(Shrink::<#n, #c >)), + (Prefix(quote!("expand/")), quote!(Expand::<#n, #c >)), + (Prefix(quote!("push/")), quote!(Push::<#n, #c >)), + (Prefix(quote!("pull/")), quote!(Pull::<#n, #c >)), + (Prefix(quote!("margin/")), quote!(Margin::<#n, #c >)), + (Prefix(quote!("padding/")), quote!(Padding::<#n, #c >)), + ].into_iter() +} + +fn _builtins_with_holes () -> impl Iterator { builtins_with(quote! { _ }, quote! { _ }) } -fn _builtins_with_boxes () -> impl Iterator { +fn _builtins_with_boxes () -> impl Iterator { builtins_with(quote! { _ }, quote! { Box> }) } -fn builtins_with_boxes_output (o: TokenStream2) -> impl Iterator { +fn builtins_with_boxes_output (o: TokenStream2) -> impl Iterator { builtins_with(quote! { _ }, quote! { Box> }) } - -fn builtins_with (n: TokenStream2, c: TokenStream2) -> impl Iterator { - [ - quote! { When::< #c > }, - quote! { Either::< #c, #c> }, - quote! { Align::< #c > }, - quote! { Bsp::< #c, #c> }, - quote! { Fill::< #c > }, - quote! { Fixed::<#n, #c > }, - quote! { Min::<#n, #c > }, - quote! { Max::<#n, #c > }, - quote! { Shrink::<#n, #c > }, - quote! { Expand::<#n, #c > }, - quote! { Push::<#n, #c > }, - quote! { Pull::<#n, #c > }, - quote! { Margin::<#n, #c > }, - quote! { Padding::<#n, #c > }, - ].into_iter() -}