diff --git a/Cargo.lock b/Cargo.lock index d9ccc29..0df7fbe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -998,7 +998,6 @@ dependencies = [ "tengri_dsl", "tengri_input", "tengri_output", - "tengri_proc", "unicode-width 0.2.0", ] diff --git a/dsl/src/dsl_iter.rs b/dsl/src/dsl_iter.rs index acdbe4b..269bc55 100644 --- a/dsl/src/dsl_iter.rs +++ b/dsl/src/dsl_iter.rs @@ -105,7 +105,7 @@ pub const fn peek_src <'a> (source: &'a str) -> Option> { }), _ => token.error(Unexpected(c)) }, - Str(_) => match c { + Str(s) => match c { '"' => return Some(token), _ => token.grow_str(), }, diff --git a/dsl/src/dsl_macros.rs b/dsl/src/dsl_macros.rs index 93b2cea..338583f 100644 --- a/dsl/src/dsl_macros.rs +++ b/dsl/src/dsl_macros.rs @@ -24,6 +24,127 @@ } } +/// Implement `Context` for one or more base structs, types, and keys. */ +#[macro_export] macro_rules! expose { + ($([$self:ident:$State:ty] $(([$($Type:tt)*] $(($pat:literal $expr:expr))*))*)*) => { + $(expose!(@impl [$self: $State] { $([$($Type)*] => { $($pat => $expr),* })* });)* + }; + ($([$self:ident:$State:ty] { $([$($Type:tt)*] => { $($pat:pat => $expr:expr),* $(,)? })* })*) => { + $(expose!(@impl [$self: $State] { $([$($Type)*] => { $($pat => $expr),* })* });)* + }; + (@impl [$self:ident:$State:ty] { $([$($Type:tt)*] => { $($pat:pat => $expr:expr),* $(,)? })* }) => { + $(expose!(@type [$($Type)*] [$self: $State] => { $($pat => $expr),* });)* + }; + (@type [bool] [$self:ident: $State:ty] => { $($pat:pat => $expr:expr),* $(,)? }) => { + provide_bool!(bool: |$self: $State| { $($pat => $expr),* }); + }; + (@type [u16] [$self:ident: $State:ty] => { $($pat:pat => $expr:expr),* $(,)? }) => { + provide_num!(u16: |$self: $State| { $($pat => $expr),* }); + }; + (@type [usize] [$self:ident: $State:ty] => { $($pat:pat => $expr:expr),* $(,)? }) => { + provide_num!(usize: |$self: $State| { $($pat => $expr),* }); + }; + (@type [isize] [$self:ident: $State:ty] => { $($pat:pat => $expr:expr),* $(,)? }) => { + provide_num!(isize: |$self: $State| { $($pat => $expr),* }); + }; + (@type [$Type:ty] [$self:ident: $State:ty] => { $($pat:pat => $expr:expr),* $(,)? }) => { + provide!($Type: |$self: $State| { $($pat => $expr),* }); + }; +} + +/// Implement `Context` for a context and type. +#[macro_export] macro_rules! provide { + // Provide a value to the EDN template + ($type:ty:|$self:ident:$State:ty|{ $($pat:pat => $expr:expr),* $(,)? }) => { + impl Context<$type> for $State { + #[allow(unreachable_code)] + fn get (&$self, dsl: &Value) -> Option<$type> { + use Value::*; + Some(match dsl { $(Sym($pat) => $expr,)* _ => return None }) + } + } + }; + // Provide a value more generically + ($lt:lifetime: $type:ty:|$self:ident:$State:ty|{ $($pat:pat => $expr:expr),* $(,)? }) => { + impl<$lt> Context<$lt, $type> for $State { + #[allow(unreachable_code)] + fn get (&$lt $self, dsl: &Value) -> Option<$type> { + use Value::*; + Some(match dsl { $(Sym($pat) => $expr,)* _ => return None }) + } + } + }; +} + +/// Implement `Context` for a context and numeric type. +/// +/// This enables support for numeric literals. +#[macro_export] macro_rules! provide_num { + // Provide a value that may also be a numeric literal in the EDN, to a generic implementation. + ($type:ty:|$self:ident:<$T:ident:$Trait:path>|{ $($pat:pat => $expr:expr),* $(,)? }) => { + impl<$T: $Trait> Context<$type> for $T { + fn get (&$self, dsl: &Value) -> Option<$type> { + use Value::*; + Some(match dsl { $(Sym($pat) => $expr,)* Num(n) => *n as $type, _ => return None }) + } + } + }; + // Provide a value that may also be a numeric literal in the EDN, to a concrete implementation. + ($type:ty:|$self:ident:$State:ty|{ $($pat:pat => $expr:expr),* $(,)? }) => { + impl Context<$type> for $State { + fn get (&$self, dsl: &Value) -> Option<$type> { + use Value::*; + Some(match dsl { $(Sym($pat) => $expr,)* Num(n) => *n as $type, _ => return None }) + } + } + }; +} + +/// Implement `Context` for a context and the boolean type. +/// +/// This enables support for boolean literals. +#[macro_export] macro_rules! provide_bool { + // Provide a value that may also be a numeric literal in the EDN, to a generic implementation. + ($type:ty:|$self:ident:<$T:ident:$Trait:path>|{ $($pat:pat => $expr:expr),* $(,)? }) => { + impl<$T: $Trait> Context<$type> for $T { + fn get (&$self, dsl: &Value) -> Option<$type> { + use Value::*; + Some(match dsl { + Num(n) => match *n { 0 => false, _ => true }, + Sym(":false") | Sym(":f") => false, + Sym(":true") | Sym(":t") => true, + $(Sym($pat) => $expr,)* + _ => return Context::get(self, dsl) + }) + } + } + }; + // Provide a value that may also be a numeric literal in the EDN, to a concrete implementation. + ($type:ty:|$self:ident:$State:ty|{ $($pat:pat => $expr:expr),* $(,)? }) => { + impl Context<$type> for $State { + fn get (&$self, dsl: &Value) -> Option<$type> { + use Value::*; + Some(match dsl { + Num(n) => match *n { 0 => false, _ => true }, + Sym(":false") | Sym(":f") => false, + Sym(":true") | Sym(":t") => true, + $(Sym($pat) => $expr,)* + _ => return None + }) + } + } + }; +} + +#[macro_export] macro_rules! impose { + ([$self:ident:$Struct:ty] $(($Command:ty : $(($cmd:literal $args:tt $result:expr))*))*) => { + $(atom_command!($Command: |$self: $Struct| { $(($cmd $args $result))* });)* + }; + ([$self:ident:$Struct:ty] { $($Command:ty => $variants:tt)* }) => { + $(atom_command!($Command: |$self: $Struct| $variants);)* + }; +} + #[macro_export] macro_rules! get_value { ($state:expr => $token:expr) => { if let Some(value) = $state.get(&$token.value) { diff --git a/dsl/src/lib.rs b/dsl/src/lib.rs index 411974a..994fc80 100644 --- a/dsl/src/lib.rs +++ b/dsl/src/lib.rs @@ -116,18 +116,16 @@ mod dsl_macros; #[cfg(test)] #[test] fn test_dsl_context () { struct Test; - #[tengri_proc::expose] - impl Test { - fn some_bool (&self) -> bool { - true - } - } - assert_eq!(Test.get(&Value::Sym(":false")), Some(false)); - assert_eq!(Test.get(&Value::Sym(":true")), Some(true)); - assert_eq!(Test.get(&Value::Sym(":some-bool")), Some(true)); - assert_eq!(Test.get(&Value::Sym(":missing-bool")), None); - assert_eq!(Test.get(&Value::Num(0)), Some(false)); - assert_eq!(Test.get(&Value::Num(1)), Some(true)); + provide_bool!(bool: |self: Test|{ + ":provide-bool" => true + }); + let test = Test; + assert_eq!(test.get(&Value::Sym(":false")), Some(false)); + assert_eq!(test.get(&Value::Sym(":true")), Some(true)); + assert_eq!(test.get(&Value::Sym(":provide-bool")), Some(true)); + assert_eq!(test.get(&Value::Sym(":missing-bool")), None); + assert_eq!(test.get(&Value::Num(0)), Some(false)); + assert_eq!(test.get(&Value::Num(1)), Some(true)); } //#[cfg(test)] #[test] fn test_examples () -> Result<(), ParseError> { diff --git a/input/src/_input.rs b/input/src/_input.rs new file mode 100644 index 0000000..24dd927 --- /dev/null +++ b/input/src/_input.rs @@ -0,0 +1,36 @@ +use crate::*; +use std::time::Duration; +use std::thread::JoinHandle; + +/// Event source +pub trait Input: Send + Sync + Sized { + /// Type of input event + type Event; + /// Result of handling input + type Handled; // TODO: make this an Option> containing the undo + /// Currently handled event + fn event (&self) -> &Self::Event; + /// Whether component should exit + fn is_done (&self) -> bool; + /// Mark component as done + fn done (&self); +} + +/// Input thread entrypoint. +pub trait InputRun { + fn run_input (engine: T, state: Self, timer: Duration) -> JoinHandle<()>; +} + +/// Handle input through a mutable reference. +pub trait Handle: Send + Sync { + fn handle (&mut self, _input: &E) -> Perhaps { + Ok(None) + } +} + +/// Handle input through an immutable reference (e.g. [Arc] or [Arc]) +pub trait HandleRef: Send + Sync { + fn handle (&self, _input: &E) -> Perhaps { + Ok(None) + } +} diff --git a/input/src/_input_event_map.rs b/input/src/_input_event_map.rs new file mode 100644 index 0000000..fc9644d --- /dev/null +++ b/input/src/_input_event_map.rs @@ -0,0 +1,73 @@ +use crate::*; + +pub struct EventMap<'a, S, I: PartialEq, C> { + pub bindings: &'a [(I, &'a dyn Fn(&S) -> Option)], + pub fallback: Option<&'a dyn Fn(&S, &I) -> Option> +} + +impl<'a, S, I: PartialEq, C> EventMap<'a, S, I, C> { + pub fn handle (&self, state: &S, input: &I) -> Option { + for (binding, handler) in self.bindings.iter() { + if input == binding { + return handler(state) + } + } + if let Some(fallback) = self.fallback { + fallback(state, input) + } else { + None + } + } +} + +#[macro_export] macro_rules! keymap { + ( + $(<$lt:lifetime>)? $KEYS:ident = |$state:ident: $State:ty, $input:ident: $Input:ty| $Command:ty + { $($key:expr => $handler:expr),* $(,)? } $(,)? + ) => { + pub const $KEYS: EventMap<'static, $State, $Input, $Command> = EventMap { + fallback: None, + bindings: &[ $(($key, &|$state|Some($handler)),)* ] + }; + input_to_command!($(<$lt>)? $Command: |$state: $State, input: $Input|$KEYS.handle($state, input)?); + }; + ( + $(<$lt:lifetime>)? $KEYS:ident = |$state:ident: $State:ty, $input:ident: $Input:ty| $Command:ty + { $($key:expr => $handler:expr),* $(,)? }, $default:expr + ) => { + pub const $KEYS: EventMap<'static, $State, $Input, $Command> = EventMap { + fallback: Some(&|$state, $input|Some($default)), + bindings: &[ $(($key, &|$state|Some($handler)),)* ] + }; + input_to_command!($(<$lt>)? $Command: |$state: $State, input: $Input|$KEYS.handle($state, input)?); + }; +} + +#[macro_export] macro_rules! input_to_command { + (<$($l:lifetime),+> $Command:ty: |$state:ident:$State:ty, $input:ident:$Input:ty| $handler:expr) => { + impl<$($l),+> InputToCommand<$Input, $State> for $Command { + fn input_to_command ($state: &$State, $input: &$Input) -> Option { + Some($handler) + } + } + }; + ($Command:ty: |$state:ident:$State:ty, $input:ident:$Input:ty| $handler:expr) => { + impl InputToCommand<$Input, $State> for $Command { + fn input_to_command ($state: &$State, $input: &$Input) -> Option { + Some($handler) + } + } + } +} + +pub trait InputToCommand: Command + Sized { + fn input_to_command (state: &S, input: &I) -> Option; + fn execute_with_state (state: &mut S, input: &I) -> Perhaps { + Ok(if let Some(command) = Self::input_to_command(state, input) { + let _undo = command.execute(state)?; + Some(true) + } else { + None + }) + } +} diff --git a/input/src/input_macros.rs b/input/src/input_macros.rs index ae5758c..c3e13e9 100644 --- a/input/src/input_macros.rs +++ b/input/src/input_macros.rs @@ -1,3 +1,30 @@ +use crate::*; + +/** Implement `Command` for given `State` and collection + * of `Variant` to `handler` mappings. */ +#[macro_export] macro_rules! defcom { + ([$self:ident, $state:ident:$State:ty] $(($Command:ident $(( + $Variant:ident [$($($param:ident: $Param:ty),+)?] $expr:expr + ))*))*) => { + $(#[derive(Clone, Debug)] pub enum $Command { + $($Variant $(($($Param),+))?),* + })* + $(command!(|$self: $Command, $state: $State|match $self { + $($Command::$Variant $(($($param),+))? => $expr),* + });)* + }; + (|$self:ident, $state:ident:$State:ty| $($Command:ident { $( + $Variant:ident $(($($param:ident: $Param:ty),+))? => $expr:expr + )* $(,)? })*) => { + $(#[derive(Clone, Debug)] pub enum $Command { + $($Variant $(($($Param),+))?),* + })* + $(command!(|$self: $Command, $state: $State|match $self { + $($Command::$Variant $(($($param),+))? => $expr),* + });)* + }; +} + /** Implement `Command` for given `State` and `handler` */ #[macro_export] macro_rules! command { ($(<$($l:lifetime),+>)?|$self:ident:$Command:ty,$state:ident:$State:ty|$handler:expr) => { @@ -8,3 +35,97 @@ } }; } + +/** Implement `DslCommand` for given `State` and `Command` */ +#[cfg(feature = "dsl")] +#[macro_export] macro_rules! atom_command { + ($Command:ty : |$state:ident:<$State:ident: $Trait:path>| { $(( + // identifier + $key:literal [ + // named parameters + $( + // argument name + $arg:ident + // if type is not provided defaults to Dsl + $( + // type:name separator + : + // argument type + $type:ty + )? + ),* + // rest of parameters + $(, ..$rest:ident)? + ] + // bound command: + $command:expr + ))* }) => { + impl<'a, $State: $Trait> TryFromDsl<'a, $State> for $Command { + fn try_from_expr ($state: &$State, iter: TokenIter) -> Option { + let iter = iter.clone(); + match iter.next() { + $(Some(Token { value: Value::Key($key), .. }) => { + let iter = iter.clone(); + $( + let next = iter.next(); + if next.is_none() { panic!("no argument: {}", stringify!($arg)); } + let $arg = next.unwrap(); + $(let $arg: Option<$type> = Context::<$type>::get($state, &$arg.value);)? + )* + $(let $rest = iter.clone();)? + return $command + },)* + _ => None + } + None + } + } + }; + ($Command:ty : |$state:ident:$State:ty| { $(( + // identifier + $key:literal [ + // named parameters + $( + // argument name + $arg:ident + // if type is not provided defaults to Dsl + $( + // type:name separator + : + // argument type + $type:ty + )? + ),* + // rest of parameters + $(, ..$rest:ident)? + ] + // bound command: + $command:expr + ))* }) => { + impl<'a> TryFromDsl<'a, $State> for $Command { + fn try_from_expr ($state: &$State, iter: TokenIter) -> Option { + let mut iter = iter.clone(); + match iter.next() { + $(Some(Token { value: Value::Key($key), .. }) => { + let mut iter = iter.clone(); + $( + let next = iter.next(); + if next.is_none() { panic!("no argument: {}", stringify!($arg)); } + let $arg = next.unwrap(); + $(let $arg: Option<$type> = Context::<$type>::get($state, &$arg.value);)? + )* + $(let $rest = iter.clone();)? + return $command + }),* + _ => None + } + } + } + }; + (@bind $state:ident =>$arg:ident ? : $type:ty) => { + let $arg: Option<$type> = Context::<$type>::get($state, $arg); + }; + (@bind $state:ident => $arg:ident : $type:ty) => { + let $arg: $type = Context::<$type>::get_or_fail($state, $arg); + }; +} diff --git a/proc/src/proc_command.rs b/proc/src/proc_command.rs index b9ececc..d3d2ec4 100644 --- a/proc/src/proc_command.rs +++ b/proc/src/proc_command.rs @@ -109,28 +109,21 @@ impl CommandArm { fn ident_to_enum_variant (ident: &Ident) -> Arc { format!("{}", AsUpperCamelCase(format!("{ident}"))).into() } - fn has_args (&self) -> bool { - self.1.len() > 1 - } - fn args (&self) -> impl Iterator)> { - self.1.iter().skip(1).filter_map(|arg|if let FnArg::Typed(PatType { - ty, pat: box Pat::Ident(PatIdent { ident: arg, .. }), .. - }) = arg { - Some((arg, ty)) - } else { - unreachable!("only typed args should be present at this position"); - None - }) - } fn to_enum_variant_def (&self) -> TokenStream2 { let mut out = TokenStream2::new(); out.append(self.to_enum_variant_ident()); let ident = &self.0; - if self.has_args() { + if self.1.len() > 2 { out.append(Group::new(Delimiter::Brace, { let mut out = TokenStream2::new(); - for (arg, ty) in self.args() { - write_quote_to(&mut out, quote! { #arg : #ty , }); + for arg in self.1.iter().skip(2) { + if let FnArg::Typed(PatType { + ty, pat: box Pat::Ident(PatIdent { ident, .. }), .. + }) = arg { + write_quote_to(&mut out, quote! { #ident : #ty , }); + } else { + unreachable!("only typed args should be present at this position") + } } out })); @@ -142,18 +135,24 @@ impl CommandArm { let mut out = TokenStream2::new(); out.append(self.to_enum_variant_ident()); let ident = &self.0; - if self.has_args() { + if self.1.len() > 2 { out.append(Group::new(Delimiter::Brace, { let mut out = TokenStream2::new(); - 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 \"{}\" ({})", - 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) , - }); + for arg in self.1.iter().skip(2) { + if let FnArg::Typed(PatType { + ty, pat: box Pat::Ident(PatIdent { ident: arg, .. }), .. + }) = arg { + 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 \"{}\" ({})", + 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) , + }); + } else { + unreachable!("only typed args should be present at this position") + } } out })); @@ -164,11 +163,17 @@ impl CommandArm { let mut out = TokenStream2::new(); out.append(self.to_enum_variant_ident()); let ident = &self.0; - if self.has_args() { + if self.1.len() > 2 { out.append(Group::new(Delimiter::Brace, { let mut out = TokenStream2::new(); - for (arg, ty) in self.args() { - write_quote_to(&mut out, quote! { #arg , }); + for arg in self.1.iter().skip(2) { + if let FnArg::Typed(PatType { + ty, pat: box Pat::Ident(PatIdent { ident: arg, .. }), .. + }) = arg { + write_quote_to(&mut out, quote! { #arg , }); + } else { + unreachable!("only typed args should be present at this position") + } } out })); @@ -176,21 +181,52 @@ impl CommandArm { out } fn to_matcher (&self) -> TokenStream2 { - let key = LitStr::new(&self.to_key(), Span::call_site()); + let mut out = TokenStream2::new(); + let key = LitStr::new(&self.to_key(), Span::call_site()); + let ident = &self.0; + let take_args = self.1.iter().skip(2).map(|arg|{ + if let FnArg::Typed(PatType { + ty, pat: box Pat::Ident(PatIdent { ident: arg, .. }), .. + }) = arg { + let take_err = LitStr::new(&format!("{}: missing argument \"{}\" ({})", + quote!{#ident}, quote!{#arg}, quote!{#ty}), Span::call_site()); + write_quote(quote! { + let #ident: #ty = Context::<#ty>::get( + state, + &iter.next().expect(#take_err).value + ); + }) + } else { + unreachable!("only typed args should be present at this position") + } + }).collect::>(); + let variant = Self::ident_to_enum_variant(&self.0); let variant = self.to_enum_variant_bind(); - let pattern = quote! { - Some(::tengri::dsl::Token { value: ::tengri::dsl::Value::Key(#key), .. }) - }; write_quote(quote! { - #pattern => { let mut iter = iter.clone(); Some(Self::#variant) }, + Some(::tengri::dsl::Token { value: ::tengri::dsl::Value::Key(#key), .. }) => { + let mut iter = iter.clone(); + //#(#take_args)* + //let rest = iter; // TODO + Some(Self::#variant) + }, }) } fn to_implementation (&self) -> TokenStream2 { let ident = &self.0; let variant = self.to_enum_variant_unbind(); let mut give_rest = write_quote(quote! { /*TODO*/ }); - let give_args = self.args().map(|(arg, ty)|write_quote(quote! { #arg, })).collect::>(); - write_quote(quote! { Self::#variant => Self::#ident(state, #(#give_args)* #give_rest), }) + let give_args = self.1.iter().skip(2).map(|arg|{ + if let FnArg::Typed(PatType { + ty, pat: box Pat::Ident(PatIdent { ident: arg, .. }), .. + }) = arg { + //let give_err = LitStr::new(&format!("{}: missing value \"{}\" ({})", + //quote!{#ident}, quote!{#pat}, quote!{#ty}), Span::call_site()); + write_quote(quote! { #arg, }) + } else { + unreachable!("only typed args should be present at this position") + } + }).collect::>(); + write_quote(quote! { Self::#variant => self.#ident(state, #(#give_args)* #give_rest), }) } } @@ -202,7 +238,7 @@ impl ToTokens for CommandVariant { out.append(Group::new(Delimiter::Parenthesis, { let mut out = TokenStream2::new(); for arg in args.iter() { - if let FnArg::Typed(PatType { ty, .. }) = arg { + if let FnArg::Typed(PatType { attrs, pat, colon_token, ty }) = arg { out.append(LitStr::new( &format!("{}", quote! { #ty }), Span::call_site() @@ -227,13 +263,14 @@ impl ToTokens for CommandArm { out.append(Group::new(Delimiter::Parenthesis, { let mut out = TokenStream2::new(); out.append(self.to_enum_variant_ident()); + for arg in args.iter() { + } out })); out.append(Punct::new('=', Joint)); out.append(Punct::new('>', Alone)); - out.append(Ident::new("Self", Span::call_site())); - out.append(Punct::new(':', Joint)); - out.append(Punct::new(':', Alone)); + out.append(Ident::new("self", Span::call_site())); + out.append(Punct::new('.', Alone)); out.append(ident.clone()); out.append(Group::new(Delimiter::Parenthesis, { let mut out = TokenStream2::new(); diff --git a/proc/src/proc_expose.rs b/proc/src/proc_expose.rs index affb192..0303509 100644 --- a/proc/src/proc_expose.rs +++ b/proc/src/proc_expose.rs @@ -7,7 +7,10 @@ pub(crate) struct ExposeDef(pub(crate) ExposeMeta, pub(crate) ExposeImpl); pub(crate) struct ExposeMeta; #[derive(Debug, Clone)] -pub(crate) struct ExposeImpl(ItemImpl, BTreeMap>); +pub(crate) struct ExposeImpl { + block: ItemImpl, + exposed: BTreeMap>, +} #[derive(Debug, Clone)] struct ExposeArm(String, Ident); @@ -46,22 +49,26 @@ impl Parse for ExposeImpl { } } } - Ok(Self(block, exposed)) + Ok(Self { block, exposed }) } } impl ToTokens for ExposeDef { fn to_tokens (&self, out: &mut TokenStream2) { - let Self(_meta, data) = self; - write_quote_to(out, quote! { #data }); + let Self(meta, data) = self; + for token in quote! { #data } { + out.append(token) + } } } impl ToTokens for ExposeImpl { fn to_tokens (&self, out: &mut TokenStream2) { - let Self(block, exposed) = self; - let target = &block.self_ty; - write_quote_to(out, quote! { #block }); + let Self { block, exposed } = self; + let target = &self.block.self_ty; + for token in quote! { #block } { + out.append(token); + } for (t, variants) in exposed.iter() { let formatted_type = format!("{}", quote! { #t }); let predefined = match formatted_type.as_str() { @@ -82,9 +89,8 @@ impl ToTokens for ExposeImpl { }, _ => quote! {}, }; - let values = variants.iter().map(ExposeArm::from); - write_quote_to(out, quote! { - /// Generated by [tengri_proc]. + let values = variants.iter().map(|(k, v)|ExposeArm(k.clone(), v.clone())); + let trait_impl = quote! { impl ::tengri::dsl::Context<#t> for #target { fn get (&self, dsl: &::tengri::dsl::Value) -> Option<#t> { Some(match dsl { @@ -94,7 +100,10 @@ impl ToTokens for ExposeImpl { }) } } - }); + }; + for token in trait_impl { + out.append(token); + } } if exposed.len() > 0 { //panic!("{}", quote! {#out}); @@ -102,19 +111,34 @@ impl ToTokens for ExposeImpl { } } -impl From<(&String, &Ident)> for ExposeArm { - fn from ((a, b): (&String, &Ident)) -> Self { - Self(a.clone(), b.clone()) - } -} - impl ToTokens for ExposeArm { fn to_tokens (&self, out: &mut TokenStream2) { 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() - }) + out.append(Punct::new(':', Joint)); + out.append(Punct::new(':', Alone)); + out.append(Ident::new("tengri", Span::call_site())); + out.append(Punct::new(':', Joint)); + out.append(Punct::new(':', Alone)); + out.append(Ident::new("dsl", Span::call_site())); + out.append(Punct::new(':', Joint)); + out.append(Punct::new(':', Alone)); + out.append(Ident::new("Value", Span::call_site())); + out.append(Punct::new(':', Joint)); + out.append(Punct::new(':', Alone)); + out.append(Ident::new("Sym", Span::call_site())); + out.append(Group::new(Delimiter::Parenthesis, { + let mut out = TokenStream2::new(); + out.append(LitStr::new(&key, Span::call_site()).token()); + out + })); + out.append(Punct::new('=', Joint)); + out.append(Punct::new('>', Alone)); + out.append(Ident::new("self", Span::call_site())); + out.append(Punct::new('.', Alone)); + for token in quote! { #value } { + out.append(token); + } + out.append(Group::new(Delimiter::Parenthesis, TokenStream2::new())); } } diff --git a/tui/Cargo.toml b/tui/Cargo.toml index a9eb4d9..5bf0dc2 100644 --- a/tui/Cargo.toml +++ b/tui/Cargo.toml @@ -20,9 +20,8 @@ tengri_output = { path = "../output" } tengri_dsl = { optional = true, path = "../dsl" } [dev-dependencies] -tengri = { path = "../tengri", features = [ "dsl" ] } -tengri_dsl = { path = "../dsl" } -tengri_proc = { path = "../proc" } +tengri = { path = "../tengri", features = [ "dsl" ] } +tengri_dsl = { path = "../dsl" } [features] dsl = [ "tengri_dsl", "tengri_input/dsl", "tengri_output/dsl" ] diff --git a/tui/examples/demo.rs.old b/tui/examples/demo.rs.old new file mode 100644 index 0000000..ba013de --- /dev/null +++ b/tui/examples/demo.rs.old @@ -0,0 +1,131 @@ +use tek::*; + +fn main () -> Usually<()> { + Tui::run(Arc::new(RwLock::new(Demo::new())))?; + Ok(()) +} + +pub struct Demo { + index: usize, + items: Vec>> +} + +impl Demo { + fn new () -> Self { + Self { + index: 0, + items: vec![] + } + } +} + +impl Content for Demo { + type Engine = Tui; + fn content (&self) -> dyn Render { + let border_style = Style::default().fg(Color::Rgb(0,0,0)); + Align::Center(Layers::new(move|add|{ + + add(&Background(Color::Rgb(0,128,128)))?; + + add(&Margin::XY(1, 1, Stack::down(|add|{ + + add(&Layers::new(|add|{ + add(&Background(Color::Rgb(128,96,0)))?; + add(&Border(Square(border_style)))?; + add(&Margin::XY(2, 1, "..."))?; + Ok(()) + }).debug())?; + + add(&Layers::new(|add|{ + add(&Background(Color::Rgb(128,64,0)))?; + add(&Border(Lozenge(border_style)))?; + add(&Margin::XY(4, 2, "---"))?; + Ok(()) + }).debug())?; + + add(&Layers::new(|add|{ + add(&Background(Color::Rgb(96,64,0)))?; + add(&Border(SquareBold(border_style)))?; + add(&Margin::XY(6, 3, "~~~"))?; + Ok(()) + }).debug())?; + + Ok(()) + })).debug())?; + + Ok(()) + + })) + //Align::Center(Margin::X(1, Layers::new(|add|{ + //add(&Background(Color::Rgb(128,0,0)))?; + //add(&Stack::down(|add|{ + //add(&Margin::Y(1, Layers::new(|add|{ + //add(&Background(Color::Rgb(0,128,0)))?; + //add(&Align::Center("12345"))?; + //add(&Align::Center("FOO")) + //})))?; + //add(&Margin::XY(1, 1, Layers::new(|add|{ + //add(&Align::Center("1234567"))?; + //add(&Align::Center("BAR"))?; + //add(&Background(Color::Rgb(0,0,128))) + //}))) + //})) + //}))) + + //Align::Y(Layers::new(|add|{ + //add(&Background(Color::Rgb(128,0,0)))?; + //add(&Margin::X(1, Align::Center(Stack::down(|add|{ + //add(&Align::X(Margin::Y(1, Layers::new(|add|{ + //add(&Background(Color::Rgb(0,128,0)))?; + //add(&Align::Center("12345"))?; + //add(&Align::Center("FOO")) + //})))?; + //add(&Margin::XY(1, 1, Layers::new(|add|{ + //add(&Align::Center("1234567"))?; + //add(&Align::Center("BAR"))?; + //add(&Background(Color::Rgb(0,0,128))) + //})))?; + //Ok(()) + //}))))) + //})) + } +} + +impl Handle for Demo { + fn handle (&mut self, from: &TuiIn) -> Perhaps { + use KeyCode::{PageUp, PageDown}; + match from.event() { + kexp!(PageUp) => { + self.index = (self.index + 1) % self.items.len(); + }, + kexp!(PageDown) => { + self.index = if self.index > 1 { + self.index - 1 + } else { + self.items.len() - 1 + }; + }, + _ => return Ok(None) + } + Ok(Some(true)) + } +} + +//lisp!(CONTENT Demo (LET + //(BORDER-STYLE (STYLE (FG (RGB 0 0 0)))) + //(BG-COLOR-0 (RGB 0 128 128)) + //(BG-COLOR-1 (RGB 128 96 0)) + //(BG-COLOR-2 (RGB 128 64 0)) + //(BG-COLOR-3 (RGB 96 64 0)) + //(CENTER (LAYERS + //(BACKGROUND BG-COLOR-0) + //(OUTSET-XY 1 1 (SPLIT-DOWN + //(LAYERS (BACKGROUND BG-COLOR-1) + //(BORDER SQUARE BORDER-STYLE) + //(OUTSET-XY 2 1 "...")) + //(LAYERS (BACKGROUND BG-COLOR-2) + //(BORDER LOZENGE BORDER-STYLE) + //(OUTSET-XY 4 2 "---")) + //(LAYERS (BACKGROUND BG-COLOR-3) + //(BORDER SQUARE-BOLD BORDER-STYLE) + //(OUTSET-XY 2 1 "~~~")))))))) diff --git a/tui/examples/tui.rs b/tui/examples/tui.rs index 3869a8e..7d9f9d7 100644 --- a/tui/examples/tui.rs +++ b/tui/examples/tui.rs @@ -41,24 +41,23 @@ handle!(TuiIn: |self: Example, input|{ }) }); -#[tengri_proc::expose] -impl Example { - //[bool] => {} - //[u16] => {} - //[usize] => {} +defcom! { |self, state: Example| + ExampleCommand { + Next => { + state.0 = (state.0 + 1) % EXAMPLES.len(); + None + } + Prev => { + state.0 = if state.0 > 0 { state.0 - 1 } else { EXAMPLES.len() - 1 }; + None + } + } } -#[tengri_proc::command(Example)] -impl ExampleCommand { - fn next (state: &mut Example) -> Perhaps { - state.0 = (state.0 + 1) % EXAMPLES.len(); - Ok(None) - } - fn prev (state: &mut Example) -> Perhaps { - state.0 = if state.0 > 0 { state.0 - 1 } else { EXAMPLES.len() - 1 }; - Ok(None) - } -} +atom_command!(ExampleCommand: |app: Example| { + ("prev" [] Some(Self::Prev)) + ("next" [] Some(Self::Next)) +}); view!(TuiOut: |self: Example|{ let index = self.0 + 1; @@ -78,3 +77,11 @@ view!(TuiOut: |self: Example|{ ":map-e" => Map::east(5u16, ||0..5u16, |n, i|format!("{n}")).boxed(), ":map-s" => Map::south(5u16, ||0..5u16, |n, i|format!("{n}")).boxed(), }); + +expose! { + [self: Example] { + [bool] => {} + [u16] => {} + [usize] => {} + } +}