wip: dsl, input, output, proc: more precise lifetimes
Some checks are pending
/ build (push) Waiting to run

This commit is contained in:
🪞👃🪞 2025-05-09 18:17:10 +03:00
parent 22d63eed9c
commit 5e09f5a4bb
11 changed files with 140 additions and 122 deletions

View file

@ -1,11 +1,17 @@
use crate::*;
pub trait TryFromDsl<'a, T>: Sized {
fn try_from_expr (_state: &'a T, _iter: TokenIter<'a>) -> Option<Self> {
pub trait TryFromDsl<'state, T>: Sized {
fn try_from_expr <'source: 'state> (
_state: &'state T, _iter: TokenIter<'source>
) -> Option<Self> {
None
}
fn try_from_atom (state: &'a T, value: Value<'a>) -> Option<Self> {
if let Exp(0, iter) = value { return Self::try_from_expr(state, iter) }
fn try_from_atom <'source: 'state> (
state: &'state T, value: Value<'source>
) -> Option<Self> {
if let Exp(0, iter) = value {
return Self::try_from_expr(state, iter.clone())
}
None
}
}
@ -15,29 +21,20 @@ pub trait TryIntoDsl<T>: Sized {
}
/// Map EDN tokens to parameters of a given type for a given context
pub trait Context<U>: Sized {
fn get (&self, _atom: &Value) -> Option<U> {
pub trait Context<'state, U>: Sized {
fn get <'source> (&'state self, _iter: &mut TokenIter<'source>) -> Option<U> {
None
}
fn get_or_fail (&self, dsl: &Value) -> U {
self.get(dsl).expect("no value")
}
impl<'state, T: Context<'state, U>, U> Context<'state, U> for &T {
fn get <'source> (&'state self, iter: &mut TokenIter<'source>) -> Option<U> {
(*self).get(iter)
}
}
impl<T: Context<U>, U> Context<U> for &T {
fn get (&self, dsl: &Value) -> Option<U> {
(*self).get(dsl)
}
fn get_or_fail (&self, dsl: &Value) -> U {
(*self).get_or_fail(dsl)
}
}
impl<T: Context<U>, U> Context<U> for Option<T> {
fn get (&self, dsl: &Value) -> Option<U> {
self.as_ref().map(|s|s.get(dsl)).flatten()
}
fn get_or_fail (&self, dsl: &Value) -> U {
self.as_ref().map(|s|s.get_or_fail(dsl)).expect("no provider")
impl<'state, T: Context<'state, U>, U> Context<'state, U> for Option<T> {
fn get <'source> (&'state self, iter: &mut TokenIter<'source>) -> Option<U> {
self.as_ref().map(|s|s.get(iter)).flatten()
}
}

View file

@ -22,38 +22,38 @@
//!```
use crate::*;
#[derive(Debug, Copy, Clone, Default, PartialEq)] pub struct Token<'a> {
pub source: &'a str,
#[derive(Debug, Copy, Clone, Default, PartialEq)] pub struct Token<'source> {
pub source: &'source str,
pub start: usize,
pub length: usize,
pub value: Value<'a>,
pub value: Value<'source>,
}
#[derive(Debug, Copy, Clone, Default, PartialEq)] pub enum Value<'a> {
#[derive(Debug, Copy, Clone, Default, PartialEq)] pub enum Value<'source> {
#[default] Nil,
Err(ParseError),
Num(usize),
Sym(&'a str),
Key(&'a str),
Str(&'a str),
Exp(usize, TokenIter<'a>),
Sym(&'source str),
Key(&'source str),
Str(&'source str),
Exp(usize, TokenIter<'source>),
}
impl<'a> Token<'a> {
pub const fn new (source: &'a str, start: usize, length: usize, value: Value<'a>) -> Self {
impl<'source> Token<'source> {
pub const fn new (source: &'source str, start: usize, length: usize, value: Value<'source>) -> Self {
Self { source, start, length, value }
}
pub const fn end (&self) -> usize {
self.start.saturating_add(self.length)
}
pub const fn slice (&'a self) -> &'a str {
pub const fn slice (&'source self) -> &'source str {
self.slice_source(self.source)
//str_range(self.source, self.start, self.end())
}
pub const fn slice_source <'b> (&'a self, source: &'b str) -> &'b str {
pub const fn slice_source <'range> (&'source self, source: &'range str) -> &'range str {
str_range(source, self.start, self.end())
}
pub const fn slice_source_exp <'b> (&'a self, source: &'b str) -> &'b str {
pub const fn slice_source_exp <'range> (&'source self, source: &'range str) -> &'range str {
str_range(source, self.start.saturating_add(1), self.end())
}
pub const fn value (&self) -> Value {

View file

@ -2,9 +2,9 @@ use crate::*;
use std::marker::PhantomData;
/// A [Command] that can be constructed from a [Token].
pub trait DslCommand<'a, C>: TryFromDsl<'a, C> + Command<C> {}
pub trait DslCommand<'state, C>: TryFromDsl<'state, C> + Command<C> {}
impl<'a, C, T: TryFromDsl<'a, C> + Command<C>> DslCommand<'a, C> for T {}
impl<'state, C, T: TryFromDsl<'state, C> + Command<C>> DslCommand<'state, C> for T {}
/// [Input] state that can be matched against a [Value].
pub trait DslInput: Input {
@ -12,14 +12,14 @@ pub trait DslInput: Input {
}
/// A pre-configured mapping of input events to commands.
pub trait KeyMap<'a, S, C: DslCommand<'a, S>, I: DslInput> {
pub trait KeyMap<'state, S, C: DslCommand<'state, S>, I: DslInput> {
/// Try to find a command that matches the current input event.
fn command (&'a self, state: &'a S, input: &'a I) -> Option<C>;
fn command (&'state self, state: &'state S, input: &'state I) -> Option<C>;
}
/// A [SourceIter] can be a [KeyMap].
impl<'a, S, C: DslCommand<'a, S>, I: DslInput> KeyMap<'a, S, C, I> for SourceIter<'a> {
fn command (&'a self, state: &'a S, input: &'a I) -> Option<C> {
impl<'state, S, C: DslCommand<'state, S>, I: DslInput> KeyMap<'state, S, C, I> for SourceIter<'state> {
fn command (&'state self, state: &'state S, input: &'state I) -> Option<C> {
let mut iter = self.clone();
while let Some((token, rest)) = iter.next() {
iter = rest;
@ -45,8 +45,8 @@ impl<'a, S, C: DslCommand<'a, S>, I: DslInput> KeyMap<'a, S, C, I> for SourceIte
}
/// A [TokenIter] can be a [KeyMap].
impl<'a, S, C: DslCommand<'a, S>, I: DslInput> KeyMap<'a, S, C, I> for TokenIter<'a> {
fn command (&'a self, state: &'a S, input: &'a I) -> Option<C> {
impl<'state, S, C: DslCommand<'state, S>, I: DslInput> KeyMap<'state, S, C, I> for TokenIter<'state> {
fn command (&'state self, state: &'state S, input: &'state I) -> Option<C> {
let mut iter = self.clone();
while let Some(next) = iter.next() {
match next {
@ -70,25 +70,25 @@ impl<'a, S, C: DslCommand<'a, S>, I: DslInput> KeyMap<'a, S, C, I> for TokenIter
}
}
pub type InputLayerCond<'a, S> = Box<dyn Fn(&S)->bool + Send + Sync + 'a>;
pub type InputLayerCond<'state, S> = Box<dyn Fn(&S)->bool + Send + Sync + 'state>;
/// A collection of pre-configured mappings of input events to commands,
/// which may be made available subject to given conditions.
pub struct InputMap<'a, S, C, I, M>
pub struct InputMap<'state, S, C, I, M>
where
C: DslCommand<'a, S>,
C: DslCommand<'state, S>,
I: DslInput,
M: KeyMap<'a, S, C, I> + Send + Sync
M: KeyMap<'state, S, C, I> + Send + Sync
{
__: &'a PhantomData<(S, C, I)>,
pub layers: Vec<(InputLayerCond<'a, S>, M)>,
__: &'state PhantomData<(S, C, I)>,
pub layers: Vec<(InputLayerCond<'state, S>, M)>,
}
impl<'a, S, C, I, M> Default for InputMap<'a, S, C, I, M>
impl<'state, S, C, I, M> Default for InputMap<'state, S, C, I, M>
where
C: DslCommand<'a, S>,
C: DslCommand<'state, S>,
I: DslInput,
M: KeyMap<'a, S, C, I> + Send + Sync
M: KeyMap<'state, S, C, I> + Send + Sync
{
fn default () -> Self {
Self {
@ -98,11 +98,11 @@ where
}
}
impl<'a, S, C, I, M> InputMap<'a, S, C, I, M>
impl<'state, S, C, I, M> InputMap<'state, S, C, I, M>
where
C: DslCommand<'a, S>,
C: DslCommand<'state, S>,
I: DslInput,
M: KeyMap<'a, S, C, I> + Send + Sync
M: KeyMap<'state, S, C, I> + Send + Sync
{
pub fn new (keymap: M) -> Self {
Self::default().layer(keymap)
@ -115,21 +115,21 @@ where
self.add_layer_if(Box::new(|_|true), keymap);
self
}
pub fn layer_if (mut self, condition: InputLayerCond<'a, S>, keymap: M) -> Self {
pub fn layer_if (mut self, condition: InputLayerCond<'state, S>, keymap: M) -> Self {
self.add_layer_if(condition, keymap);
self
}
pub fn add_layer_if (&mut self, condition: InputLayerCond<'a, S>, keymap: M) -> &mut Self {
pub fn add_layer_if (&mut self, condition: InputLayerCond<'state, S>, keymap: M) -> &mut Self {
self.layers.push((Box::new(condition), keymap));
self
}
}
impl<'a, S, C, I, M> std::fmt::Debug for InputMap<'a, S, C, I, M>
impl<'state, S, C, I, M> std::fmt::Debug for InputMap<'state, S, C, I, M>
where
C: DslCommand<'a, S>,
C: DslCommand<'state, S>,
I: DslInput,
M: KeyMap<'a, S, C, I> + Send + Sync
M: KeyMap<'state, S, C, I> + Send + Sync
{
fn fmt (&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
write!(f, "[InputMap: {} layer(s)]", self.layers.len());
@ -138,13 +138,13 @@ where
}
/// An [InputMap] can be a [KeyMap].
impl<'a, S, C, I, M> KeyMap<'a, S, C, I> for InputMap<'a, S, C, I, M>
impl<'state, S, C, I, M> KeyMap<'state, S, C, I> for InputMap<'state, S, C, I, M>
where
C: DslCommand<'a, S>,
C: DslCommand<'state, S>,
I: DslInput,
M: KeyMap<'a, S, C, I> + Send + Sync
M: KeyMap<'state, S, C, I> + Send + Sync
{
fn command (&'a self, state: &'a S, input: &'a I) -> Option<C> {
fn command (&'state self, state: &'state S, input: &'state I) -> Option<C> {
for (condition, keymap) in self.layers.iter() {
if !condition(state) {
continue

View file

@ -36,7 +36,7 @@ pub enum Alignment { #[default] Center, X, Y, NW, N, NE, E, SE, S, SW, W }
pub struct Align<A>(Alignment, A);
#[cfg(feature = "dsl")]
try_from_expr!(<'a, E>: Align<RenderBox<'a, E>>: |state, iter|{
try_from_expr!(<'source, 'state, E>: Align<RenderBox<'state, E>>: |state, iter|{
if let Some(Token { value: Value::Key(key), .. }) = iter.peek() {
match key {
"align/c"|"align/x"|"align/y"|

View file

@ -17,7 +17,7 @@ impl<E: Output, A: Content<E>, B: Content<E>> Content<E> for Bsp<A, B> {
}
}
#[cfg(feature = "dsl")]
try_from_expr!(<'a, E>: Bsp<RenderBox<'a, E>, RenderBox<'a, E>>: |state, iter| {
try_from_expr!(<'source, 'state, E>: Bsp<RenderBox<'state, E>, RenderBox<'state, E>>: |state, iter| {
if let Some(Token { value: Value::Key(key), .. }) = iter.peek() {
match key {
"bsp/n"|"bsp/s"|"bsp/e"|"bsp/w"|"bsp/a"|"bsp/b" => {

View file

@ -19,12 +19,12 @@ impl<A, B> Either<A, B> {
}
#[cfg(feature = "dsl")]
try_from_expr!(<'a, E>: When<RenderBox<'a, E>>: |state, iter| {
try_from_expr!(<'source, 'state, E>: When<RenderBox<'state, E>>: |state, iter| {
if let Some(Token { value: Value::Key("when"), .. }) = iter.peek() {
let _ = iter.next().unwrap();
let condition = iter.next().expect("no condition specified");
let condition = state.get(&condition.value).expect("no condition provided");
let condition = state.get(&mut iter).expect("no condition provided");
let content = iter.next().expect("no content specified");
let content = if let Some(content) = state.get_content(&content.value) {
@ -38,12 +38,12 @@ try_from_expr!(<'a, E>: When<RenderBox<'a, E>>: |state, iter| {
});
#[cfg(feature = "dsl")]
try_from_expr!(<'a, E>: Either<RenderBox<'a, E>, RenderBox<'a, E>>: |state, iter| {
try_from_expr!(<'source, 'state, E>: Either<RenderBox<'state, E>, RenderBox<'state, E>>: |state, iter| {
if let Some(Token { value: Value::Key("either"), .. }) = iter.peek() {
let _ = iter.next().unwrap();
let condition = iter.next().expect("no condition specified");
let condition = state.get(&condition.value).expect("no condition provided");
let condition = state.get(&mut iter).expect("no condition provided");
let content = iter.next().expect("no content specified");
let content = if let Some(content) = state.get_content(&content.value) {

View file

@ -33,9 +33,11 @@ macro_rules! transform_xy {
#[inline] pub const fn xy (item: T) -> Self { Self::XY(item) }
}
#[cfg(feature = "dsl")]
impl<'a, E: Output + 'a, T: ViewContext<'a, E>> TryFromDsl<'a, T>
for $Enum<RenderBox<'a, E>> {
fn try_from_expr (state: &'a T, iter: TokenIter<'a>) -> Option<Self> {
impl<'state, E: Output + 'state, T: ViewContext<'state, E>> TryFromDsl<'state, T>
for $Enum<RenderBox<'state, E>> {
fn try_from_expr <'source: 'state> (state: &'state T, iter: TokenIter<'source>)
-> Option<Self>
{
let mut iter = iter.clone();
if let Some(Token { value: Value::Key(k), .. }) = iter.peek() {
if k == $x || k == $y || k == $xy {
@ -80,17 +82,16 @@ macro_rules! transform_xy_unit {
#[inline] pub const fn xy (x: U, y: U, item: T) -> Self { Self::XY(x, y, item) }
}
#[cfg(feature = "dsl")]
impl<'a, E: Output + 'a, T: ViewContext<'a, E>> TryFromDsl<'a, T>
for $Enum<E::Unit, RenderBox<'a, E>> {
fn try_from_expr (state: &'a T, iter: TokenIter<'a>) -> Option<Self> {
impl<'state, E: Output + 'state, T: ViewContext<'state, E>> TryFromDsl<'state, T>
for $Enum<E::Unit, RenderBox<'state, E>> {
fn try_from_expr <'source: 'state> (state: &'state T, iter: TokenIter<'source>) -> Option<Self> {
let mut iter = iter.clone();
if let Some(Token { value: Value::Key(k), .. }) = iter.peek() {
if k == $x || k == $y {
let _ = iter.next().unwrap();
let u = iter.next().expect("no unit specified");
let u = get_value!(state => u);
let c = iter.next().expect("no content specified");
let c = get_content!(state => c);
let u = state.get(&mut iter).expect("no unit specified");
let c = state.get_content(&iter.next().expect("no content specified").value)
.expect("no content provided");
return Some(match k {
$x => Self::x(u, c),
$y => Self::y(u, c),
@ -98,9 +99,10 @@ macro_rules! transform_xy_unit {
})
} else if k == $xy {
let _ = iter.next().unwrap();
let u = get_value!(state => iter.next().expect("no unit specified"));
let v = get_value!(state => iter.next().expect("no unit specified"));
let c = get_content!(state => iter.next().expect("no content specified"));
let u = state.get(&mut iter).expect("no unit specified");
let v = state.get(&mut iter).expect("no unit specified");
let c = state.get_content(&iter.next().expect("no content specified").value)
.expect("no content provided");
return Some(Self::xy(u, v, c))
}
}

View file

@ -45,34 +45,37 @@ impl<'a, O: Output + 'a, T: ViewContext<'a, O>> Content<O> for View<'a, T> {
// Provides components to the view.
#[cfg(feature = "dsl")]
pub trait ViewContext<'a, E: Output + 'a>: Send + Sync
+ Context<bool>
+ Context<usize>
+ Context<E::Unit>
pub trait ViewContext<'state, E: Output + 'state>: Send + Sync
+ Context<'state, bool>
+ Context<'state, usize>
+ Context<'state, E::Unit>
{
fn get_content (&'a self, value: &Value<'a>) -> Option<RenderBox<'a, E>> {
fn get_content <'source: 'state> (&'state self, value: &Value<'source>) -> Option<RenderBox<'state, E>> {
match value {
Value::Sym(_) => self.get_content_sym(value),
Value::Exp(_, _) => self.get_content_exp(value),
_ => panic!("only :symbols and (expressions) accepted here")
}
}
fn get_content_sym (&'a self, value: &Value<'a>) -> Option<RenderBox<'a, E>>;
fn get_content_exp (&'a self, value: &Value<'a>) -> Option<RenderBox<'a, E>> {
try_delegate!(self, *value, When::<RenderBox<'a, E>>);
try_delegate!(self, *value, Either::<RenderBox<'a, E>, RenderBox<'a, E>>);
try_delegate!(self, *value, Align::<RenderBox<'a, E>>);
try_delegate!(self, *value, Bsp::<RenderBox<'a, E>, RenderBox<'a, E>>);
try_delegate!(self, *value, Fill::<RenderBox<'a, E>>);
try_delegate!(self, *value, Fixed::<_, RenderBox<'a, E>>);
try_delegate!(self, *value, Min::<_, RenderBox<'a, E>>);
try_delegate!(self, *value, Max::<_, RenderBox<'a, E>>);
try_delegate!(self, *value, Shrink::<_, RenderBox<'a, E>>);
try_delegate!(self, *value, Expand::<_, RenderBox<'a, E>>);
try_delegate!(self, *value, Push::<_, RenderBox<'a, E>>);
try_delegate!(self, *value, Pull::<_, RenderBox<'a, E>>);
try_delegate!(self, *value, Margin::<_, RenderBox<'a, E>>);
try_delegate!(self, *value, Padding::<_, RenderBox<'a, E>>);
fn get_content_sym <'source: 'state> (&'state self, value: &Value<'source>)
-> Option<RenderBox<'state, E>>;
fn get_content_exp <'source: 'state> (&'state self, value: &Value<'source>)
-> Option<RenderBox<'state, E>>
{
try_delegate!(self, *value, When::<RenderBox<'state, E>>);
try_delegate!(self, *value, Either::<RenderBox<'state, E>, RenderBox<'state, E>>);
try_delegate!(self, *value, Align::<RenderBox<'state, E>>);
try_delegate!(self, *value, Bsp::<RenderBox<'state, E>, RenderBox<'state, E>>);
try_delegate!(self, *value, Fill::<RenderBox<'state, E>>);
try_delegate!(self, *value, Fixed::<_, RenderBox<'state, E>>);
try_delegate!(self, *value, Min::<_, RenderBox<'state, E>>);
try_delegate!(self, *value, Max::<_, RenderBox<'state, E>>);
try_delegate!(self, *value, Shrink::<_, RenderBox<'state, E>>);
try_delegate!(self, *value, Expand::<_, RenderBox<'state, E>>);
try_delegate!(self, *value, Push::<_, RenderBox<'state, E>>);
try_delegate!(self, *value, Pull::<_, RenderBox<'state, E>>);
try_delegate!(self, *value, Margin::<_, RenderBox<'state, E>>);
try_delegate!(self, *value, Padding::<_, RenderBox<'state, E>>);
None
}
}
@ -88,9 +91,20 @@ pub trait ViewContext<'a, E: Output + 'a>: Send + Sync
#[cfg(feature = "dsl")]
#[macro_export] macro_rules! try_from_expr {
(<$l:lifetime, $E:ident>: $Struct:ty: |$state:ident, $iter:ident|$body:expr) => {
impl<$l, $E: Output + $l, T: ViewContext<$l, $E>> TryFromDsl<$l, T> for $Struct {
fn try_from_expr ($state: &$l T, $iter: TokenIter<'a>) -> Option<Self> {
(<
$lt_source:lifetime,
$lt_state:lifetime,
$Output:ident
>: $Struct:ty: |$state:ident, $iter:ident|$body:expr) => {
impl<
$lt_state,
$Output: Output + $lt_state,
T: ViewContext<$lt_state, $Output>
> TryFromDsl<$lt_state, T> for $Struct {
fn try_from_expr <$lt_source: $lt_state> (
$state: &$lt_state T,
$iter: TokenIter<$lt_source>
) -> Option<Self> {
let mut $iter = $iter.clone();
$body;
None

View file

@ -68,8 +68,10 @@ impl ToTokens for CommandDef {
}
#block
/// Generated by [tengri_proc].
impl<'a> TryFromDsl<'a, #target> for #enumeration {
fn try_from_expr (state: &#target, iter: TokenIter) -> Option<Self> {
impl<'state> TryFromDsl<'state, #target> for #enumeration {
fn try_from_expr <'source: 'state> (
state: &'state #target, iter: TokenIter<'source>
) -> Option<Self> {
let mut iter = iter.clone();
match iter.next() {
#(#matchers)*
@ -148,11 +150,10 @@ impl CommandArm {
for (arg, ty) in self.args() {
let take_err = LitStr::new(&format!("{}: missing argument \"{}\" ({})",
quote!{#ident}, quote!{#arg}, quote!{#ty}), Span::call_site());
let give_err = LitStr::new(&format!("{}: missing value \"{}\" ({})",
let give_err = LitStr::new(&format!("{}: missing value for \"{}\" ({})",
quote!{#ident}, quote!{#arg}, quote!{#ty}), Span::call_site());
write_quote_to(&mut out, quote! {
#arg : Context::get(state, &iter.next().expect(#take_err).value)
.expect(#give_err) ,
#arg: Context::get(state, &mut iter).expect(#give_err),
});
}
out

View file

@ -66,8 +66,8 @@ impl ToTokens for ExposeImpl {
let formatted_type = format!("{}", quote! { #t });
let predefined = match formatted_type.as_str() {
"bool" => quote! {
::tengri::dsl::Value::Sym(":true") => true,
::tengri::dsl::Value::Sym(":false") => false,
Some(::tengri::dsl::Value::Sym(":true")) => true,
Some(::tengri::dsl::Value::Sym(":false")) => false,
},
"u8" | "u16" | "u32" | "u64" | "usize" |
"i8" | "i16" | "i32" | "i64" | "isize" => {
@ -76,7 +76,7 @@ impl ToTokens for ExposeImpl {
Span::call_site()
);
quote! {
::tengri::dsl::Value::Num(n) => TryInto::<#t>::try_into(*n)
Some(::tengri::dsl::Value::Num(n)) => TryInto::<#t>::try_into(n)
.unwrap_or_else(|_|panic!(#num_err)),
}
},
@ -85,9 +85,11 @@ impl ToTokens for ExposeImpl {
let values = variants.iter().map(ExposeArm::from);
write_quote_to(out, quote! {
/// Generated by [tengri_proc].
impl ::tengri::dsl::Context<#t> for #target {
fn get (&self, dsl: &::tengri::dsl::Value) -> Option<#t> {
Some(match dsl {
impl<'state> ::tengri::dsl::Context<'state, #t> for #target {
fn get <'source> (
&self, iter: &mut ::tengri::dsl::TokenIter<'source>
) -> Option<#t> {
Some(match iter.next().map(|x|x.value) {
#predefined
#(#values,)*
_ => return None
@ -113,7 +115,7 @@ impl ToTokens for ExposeArm {
let Self(key, value) = self;
let key = LitStr::new(&key, Span::call_site());
write_quote_to(out, quote! {
::tengri::dsl::Value::Sym(#key) => self.#value()
Some(::tengri::dsl::Value::Sym(#key)) => self.#value()
})
}
}

View file

@ -55,8 +55,10 @@ impl ToTokens for ViewDef {
}
}
/// Generated by [tengri_proc].
impl<'a> ::tengri::output::ViewContext<'a, #output> for #ident {
fn get_content_sym (&'a self, value: &Value<'a>) -> Option<RenderBox<'a, #output>> {
impl<'state> ::tengri::output::ViewContext<'state, #output> for #ident {
fn get_content_sym <'source: 'state> (&'state self, value: &Value<'source>)
-> Option<RenderBox<'state, #output>>
{
match value {
#(#exposed)*
_ => panic!("expected Sym(content), got: {value:?}")