wip: slowly remembering where i was
Some checks failed
/ build (push) Has been cancelled

This commit is contained in:
🪞👃🪞 2025-06-13 11:42:20 +03:00
parent 17506726cb
commit c8827b43c3
9 changed files with 114 additions and 135 deletions

View file

@ -1,4 +1,4 @@
use std::error::Error; pub(crate) use std::error::Error;
/// Standard result type. /// Standard result type.
pub type Usually<T> = Result<T, Box<dyn Error>>; pub type Usually<T> = Result<T, Box<dyn Error>>;
@ -16,29 +16,56 @@ pub type Perhaps<T> = Result<Option<T>, Box<dyn Error>>;
} }
pub trait Has<T>: Send + Sync { pub trait Has<T>: Send + Sync {
fn get (&self) -> &T; fn get (&self) -> &T;
fn get_mut (&mut self) -> &mut T; fn get_mut (&mut self) -> &mut T;
} }
#[macro_export] macro_rules! has { #[macro_export] macro_rules! has {
($T:ty: |$self:ident : $S:ty| $x:expr) => { ($T:ty: |$self:ident : $S:ty| $x:expr) => {
impl Has<$T> for $S { impl Has<$T> for $S {
fn get (&$self) -> &$T { &$x } fn get (&$self) -> &$T { &$x }
fn get_mut (&mut $self) -> &mut $T { &mut $x } fn get_mut (&mut $self) -> &mut $T { &mut $x }
} }
}; };
} }
pub trait MaybeHas<T>: Send + Sync { pub trait MaybeHas<T>: Send + Sync {
fn get (&self) -> Option<&T>; fn get (&self) -> Option<&T>;
fn get_mut (&mut self) -> Option<&mut T>; fn get_mut (&mut self) -> Option<&mut T>;
} }
#[macro_export] macro_rules! maybe_has { #[macro_export] macro_rules! maybe_has {
($T:ty: |$self:ident : $S:ty| $x:block; $y:block $(;)?) => { ($T:ty: |$self:ident : $S:ty| $x:block; $y:block $(;)?) => {
impl MaybeHas<$T> for $S { impl MaybeHas<$T> for $S {
fn get (&$self) -> Option<&$T> $x fn get (&$self) -> Option<&$T> $x
fn get_mut (&mut $self) -> Option<&mut $T> $y fn get_mut (&mut $self) -> Option<&mut $T> $y
} }
}; };
} }
/// May compute a `RetVal` from `Args`.
pub trait Eval<Args, RetVal> {
/// A custom operation on [Args] that may return [Result::Err] or [Option::None].
fn try_eval (&self, args: Args) -> Perhaps<RetVal>;
/// Invoke a custom operation, converting a `None` result to a custom `Box<dyn Error>`.
fn eval <E: Into<Box<dyn std::error::Error>>> (&self, args: Args, error: impl Fn()->E)
-> Usually<RetVal>
{
match self.try_eval(args)? {
Some(value) => Ok(value),
_ => Result::Err(format!("Eval: {}", error().into()).into())
}
}
}
//impl<S: Eval<I, O>, I, O> Eval<I, O> for &S {
//fn try_eval (&self, input: I) -> Perhaps<O> {
//(*self).try_eval(input)
//}
//}
//impl<S: Eval<I, O>, I: Ast, O: Dsl<S>> Eval<I, O> for S {
//fn try_eval (&self, input: I) -> Perhaps<O> {
//Dsl::try_provide(self, input)
//}
//}

View file

@ -1,30 +1,29 @@
use crate::*; use crate::*;
use std::fmt::{Display, Formatter, Error as FormatError};
impl Display for Ast { impl Display for Ast {
fn fmt (&self, out: &mut Formatter) -> Result<(), FormatError> { fn fmt (&self, out: &mut Formatter) -> Result<(), FormatError> {
use Value::*; match &self.0 {
write!(out, "{}", match &self.0 { Value::Nil => Ok(()),
Nil => String::new(), Value::Err(e) => write!(out, "[error: {e}]"),
Err(e) => format!("[error: {e}]"), Value::Num(n) => write!(out, "{n}"),
Num(n) => format!("{n}"), Value::Sym(s) => write!(out, "{s}"),
Sym(s) => format!("{s}"), Value::Key(s) => write!(out, "{s}"),
Key(s) => format!("{s}"), Value::Str(s) => write!(out, "{s}"),
Str(s) => format!("{s}"), Value::Exp(_, e) => write!(out, "{e:?}"),
Exp(_, e) => format!("{e:?}"), }
})
} }
} }
impl<'source> Display for CstValue<'source> { impl<'source> Display for CstValue<'source> {
fn fmt (&self, out: &mut Formatter) -> Result<(), FormatError> { fn fmt (&self, out: &mut Formatter) -> Result<(), FormatError> {
use Value::*; match self {
write!(out, "{}", match self { Value::Nil => Ok(()),
Nil => String::new(), Value::Err(e) => write!(out, "[error: {e}]"),
Err(e) => format!("[error: {e}]"), Value::Num(n) => write!(out, "{n}"),
Num(n) => format!("{n}"), Value::Sym(s) => write!(out, "{s}"),
Sym(s) => format!("{s}"), Value::Key(s) => write!(out, "{s}"),
Key(s) => format!("{s}"), Value::Str(s) => write!(out, "{s}"),
Str(s) => format!("{s}"), Value::Exp(_, e) => write!(out, "{e:?}"),
Exp(_, e) => format!("{e:?}"), }
})
} }
} }

View file

@ -1,84 +0,0 @@
use crate::*;
pub trait Eval<Input, Output> {
fn try_eval (&self, input: Input) -> Perhaps<Output>;
fn eval <E: Into<Box<dyn std::error::Error>>, F: Fn()->E> (
&self, input: Input, error: F
) -> Usually<Output> {
if let Some(value) = self.try_eval(input)? {
Ok(value)
} else {
Result::Err(format!("Eval: {}", error().into()).into())
}
}
}
//impl<S: Eval<I, O>, I, O> Eval<I, O> for &S {
//fn try_eval (&self, input: I) -> Perhaps<O> {
//(*self).try_eval(input)
//}
//}
//impl<S: Eval<I, O>, I: Ast, O: Dsl<S>> Eval<I, O> for S {
//fn try_eval (&self, input: I) -> Perhaps<O> {
//Dsl::try_provide(self, input)
//}
//}
/// May construct [Self] from token stream.
pub trait Dsl<State>: Sized {
fn try_provide (state: &State, source: Ast) -> Perhaps<Self>;
fn provide <E: Into<Box<dyn std::error::Error>>, F: Fn()->E> (
state: &State, source: Ast, error: F
) -> Usually<Self> {
let next = format!("{source:?}");
if let Some(value) = Self::try_provide(state, source)? {
Ok(value)
} else {
Result::Err(format!("Dsl: {}: {next:?}", error().into()).into())
}
}
}
//pub trait Give<'state, 'source, Type> {
///// Implement this to be able to [Give] [Type] from the [Cst].
///// Advance the stream if returning `Ok<Some<Type>>`.
//fn give (&'state self, words: Cst<'source>) -> Perhaps<Type>;
///// Return custom error on [None].
//fn give_or_fail <E: Into<Box<dyn std::error::Error>>, F: Fn()->E> (
//&'state self, mut words: Cst<'source>, error: F
//) -> Usually<Type> {
//let next = words.peek().map(|x|x.value).clone();
//if let Some(value) = Give::<Type>::give(self, words)? {
//Ok(value)
//} else {
//Result::Err(format!("give: {}: {next:?}", error().into()).into())
//}
//}
//}
//#[macro_export] macro_rules! give {
//($Type:ty|$state:ident:$State:ident,$words:ident|$expr:expr) => {
//impl Give<$Type> for $State {
//fn give (&self, mut $words: Cst) -> Perhaps<$Type> {
//let $state = self;
//$expr
//}
//}
//};
//($Type:path$(,$Arg:ident)*|$state:ident,$words:ident|$expr:expr) => {
//impl<State: $(Give<$Arg>)++ $(, $Arg)*> Give<$Type> for State {
//fn give (&self, mut $words: Cst) -> Perhaps<$Type> {
//let $state = self;
//$expr
//}
//}
//}
//}
/////// Implement the [Give] trait, which boils down to
/////// specifying two types and providing an expression.
//#[macro_export] macro_rules! from_dsl {
//($Type:ty: |$state:ident:$State:ty, $words:ident|$expr:expr) => {
//give! { $Type|$state:$State,$words|$expr }
//};
//}

View file

@ -1,5 +1,20 @@
use crate::*; use crate::*;
use std::fmt::Display;
/// Thing that may construct itself from state and [DslValue].
pub trait Dsl<State>: Sized {
fn try_provide (state: &State, value: impl DslValue) -> Perhaps<Self>;
fn provide (
state: &State,
value: impl DslValue,
error: impl Fn()->Box<dyn std::error::Error>
) -> Usually<Self> {
match Self::try_provide(state, value)? {
Some(value) => Ok(value),
_ => Err(error())
}
}
}
pub trait DslValue: PartialEq + Clone + Default + Debug { pub trait DslValue: PartialEq + Clone + Default + Debug {
type Str: AsRef<str> + PartialEq + Clone + Default + Debug; type Str: AsRef<str> + PartialEq + Clone + Default + Debug;
type Exp: PartialEq + Clone + Default + Debug; type Exp: PartialEq + Clone + Default + Debug;
@ -29,6 +44,7 @@ pub trait DslValue: PartialEq + Clone + Default + Debug {
fn exp_head (&self) -> Option<&Self> { None } // TODO fn exp_head (&self) -> Option<&Self> { None } // TODO
fn exp_tail (&self) -> Option<&[Self]> { None } // TODO fn exp_tail (&self) -> Option<&[Self]> { None } // TODO
} }
impl< impl<
Str: AsRef<str> + PartialEq + Clone + Default + Debug, Str: AsRef<str> + PartialEq + Clone + Default + Debug,
Exp: PartialEq + Clone + Default + Debug, Exp: PartialEq + Clone + Default + Debug,
@ -40,8 +56,20 @@ impl<
} }
} }
pub enum Value<S, X> { Nil, Err(DslError), Num(usize), Sym(S), Key(S), Str(S), Exp(usize, X), } pub enum Value<S, X> {
impl<S, X> Default for Value<S, X> { fn default () -> Self { Self:: Nil } } Nil,
Err(DslError),
Num(usize),
Sym(S),
Key(S),
Str(S),
Exp(usize, X),
}
impl<S, X> Default for Value<S, X> {
fn default () -> Self { Self:: Nil }
}
impl<S: PartialEq, X: PartialEq,> PartialEq for Value<S, X> { impl<S: PartialEq, X: PartialEq,> PartialEq for Value<S, X> {
fn eq (&self, other: &Self) -> bool { fn eq (&self, other: &Self) -> bool {
use Value::*; use Value::*;
@ -57,6 +85,7 @@ impl<S: PartialEq, X: PartialEq,> PartialEq for Value<S, X> {
} }
} }
} }
impl<S: Clone, X: Clone,> Clone for Value<S, X> { impl<S: Clone, X: Clone,> Clone for Value<S, X> {
fn clone (&self) -> Self { fn clone (&self) -> Self {
use Value::*; use Value::*;
@ -71,12 +100,15 @@ impl<S: Clone, X: Clone,> Clone for Value<S, X> {
} }
} }
} }
impl<S: Copy, X: Copy,> Copy for Value<S, X> {} impl<S: Copy, X: Copy,> Copy for Value<S, X> {}
impl<S: Debug, X: Debug,> Debug for Value<S, X> { impl<S: Debug, X: Debug,> Debug for Value<S, X> {
fn fmt (&self, _f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { fn fmt (&self, _f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
todo!() todo!()
} }
} }
impl<S: Display, X: Display,> Display for Value<S, X> { impl<S: Display, X: Display,> Display for Value<S, X> {
fn fmt (&self, _f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { fn fmt (&self, _f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
todo!() todo!()

View file

@ -36,6 +36,7 @@
#![feature(impl_trait_in_fn_trait_return)] #![feature(impl_trait_in_fn_trait_return)]
pub(crate) use ::tengri_core::*; pub(crate) use ::tengri_core::*;
pub(crate) use std::fmt::Debug; pub(crate) use std::fmt::Debug;
pub(crate) use std::fmt::{Display, Formatter, Error as FormatError};
pub(crate) use konst::iter::{ConstIntoIter, IsIteratorKind}; pub(crate) use konst::iter::{ConstIntoIter, IsIteratorKind};
pub(crate) use konst::string::{split_at, str_range, char_indices}; pub(crate) use konst::string::{split_at, str_range, char_indices};
pub(crate) use thiserror::Error; pub(crate) use thiserror::Error;
@ -43,7 +44,6 @@ pub(crate) use self::DslError::*;
mod dsl_ast; pub use self::dsl_ast::*; mod dsl_ast; pub use self::dsl_ast::*;
mod dsl_cst; pub use self::dsl_cst::*; mod dsl_cst; pub use self::dsl_cst::*;
mod dsl_display; //pub use self::dsl_display::*; mod dsl_display; //pub use self::dsl_display::*;
mod dsl_domain; pub use self::dsl_domain::*;
mod dsl_error; pub use self::dsl_error::*; mod dsl_error; pub use self::dsl_error::*;
mod dsl_iter; pub use self::dsl_iter::*; mod dsl_iter; pub use self::dsl_iter::*;
mod dsl_token; pub use self::dsl_token::*; mod dsl_token; pub use self::dsl_token::*;

View file

@ -4,7 +4,10 @@ use std::fmt::Debug;
#[derive(Default, Debug)] pub struct InputLayers(Vec<InputLayer>); #[derive(Default, Debug)] pub struct InputLayers(Vec<InputLayer>);
#[derive(Default, Debug)] pub struct InputLayer(Option<Ast>, Ast); #[derive(Default, Debug)] pub struct InputLayer {
condition: Option<Ast>,
bindings: Ast,
}
impl InputLayers { impl InputLayers {
pub fn new (layer: Ast) -> Self { pub fn new (layer: Ast) -> Self {
@ -19,8 +22,8 @@ impl InputLayers {
pub fn add_layer (&mut self, layer: Ast) -> &mut Self { pub fn add_layer (&mut self, layer: Ast) -> &mut Self {
self.add_layer_if(None, layer.into()); self self.add_layer_if(None, layer.into()); self
} }
pub fn add_layer_if (&mut self, condition: Option<Ast>, binding: Ast) -> &mut Self { pub fn add_layer_if (&mut self, condition: Option<Ast>, bindings: Ast) -> &mut Self {
self.0.push(InputLayer(condition, binding)); self.0.push(InputLayer { condition, bindings });
self self
} }
pub fn handle <S: Eval<Ast, bool> + Eval<Ast, O>, I: Eval<Ast, bool>, O: Command<S>> (&self, state: &mut S, input: I) -> Perhaps<O> { pub fn handle <S: Eval<Ast, bool> + Eval<Ast, O>, I: Eval<Ast, bool>, O: Command<S>> (&self, state: &mut S, input: I) -> Perhaps<O> {
@ -33,13 +36,13 @@ pub struct InputHandle<'a, S>(&'a mut S, &'a [InputLayer]);
impl<'a, S: Eval<Ast, bool> + Eval<Ast, O>, I: Eval<Ast, bool>, O: Command<S>> Eval<I, O> for InputHandle<'a, S> { impl<'a, S: Eval<Ast, bool> + Eval<Ast, O>, I: Eval<Ast, bool>, O: Command<S>> Eval<I, O> for InputHandle<'a, S> {
fn try_eval (&self, input: I) -> Perhaps<O> { fn try_eval (&self, input: I) -> Perhaps<O> {
let Self(state, layers) = self; let Self(state, layers) = self;
for InputLayer(condition, binding) in layers.iter() { for InputLayer { condition, bindings } in layers.iter() {
let mut matches = true; let mut matches = true;
if let Some(condition) = condition { if let Some(condition) = condition {
matches = state.eval(condition.clone(), ||"input: no condition")?; matches = state.eval(condition.clone(), ||"input: no condition")?;
} }
if matches if matches
&& let Some(exp) = binding.exp() && let Some(exp) = bindings.exp()
&& let Some(ast) = exp.peek() && let Some(ast) = exp.peek()
&& input.eval(ast.clone(), ||"InputLayers: input.eval(binding) failed")? && input.eval(ast.clone(), ||"InputLayers: input.eval(binding) failed")?
&& let Some(command) = state.try_eval(ast)? { && let Some(command) = state.try_eval(ast)? {

View file

@ -2,8 +2,9 @@ use crate::*;
use Value::*; use Value::*;
impl<S, A> Dsl<S> for When<A> where S: Eval<Ast, bool> + Eval<Ast, A> { impl<S, A> Dsl<S> for When<A> where S: Eval<Ast, bool> + Eval<Ast, A> {
fn try_provide (state: &S, source: Ast) -> Perhaps<Self> { fn try_provide (state: &S, source: impl DslValue) -> Perhaps<Self> {
if let Exp(_, mut exp) = source.0 && let Some(Ast(Key(id))) = exp.peek() && *id == *"when" { if let Exp(_, mut exp) = source.value()
&& let Some(Ast(Key(id))) = exp.peek() && *id == *"when" {
let _ = exp.next(); let _ = exp.next();
return Ok(Some(Self( return Ok(Some(Self(
state.eval(exp.next().unwrap(), ||"when: expected condition")?, state.eval(exp.next().unwrap(), ||"when: expected condition")?,
@ -15,8 +16,9 @@ impl<S, A> Dsl<S> for When<A> where S: Eval<Ast, bool> + Eval<Ast, A> {
} }
impl<S, A, B> Dsl<S> for Either<A, B> where S: Eval<Ast, bool> + Eval<Ast, A> + Eval<Ast, B> { impl<S, A, B> Dsl<S> for Either<A, B> where S: Eval<Ast, bool> + Eval<Ast, A> + Eval<Ast, B> {
fn try_provide (state: &S, source: Ast) -> Perhaps<Self> { fn try_provide (state: &S, source: impl DslValue) -> Perhaps<Self> {
if let Exp(_, mut exp) = source.0 && let Some(Ast(Key(id))) = exp.peek() && *id == *"either" { if let Exp(_, mut exp) = source.value()
&& let Some(Ast(Key(id))) = exp.peek() && *id == *"either" {
let _ = exp.next(); let _ = exp.next();
return Ok(Some(Self( return Ok(Some(Self(
state.eval(exp.next().unwrap(), ||"either: expected condition")?, state.eval(exp.next().unwrap(), ||"either: expected condition")?,
@ -29,8 +31,8 @@ impl<S, A, B> Dsl<S> for Either<A, B> where S: Eval<Ast, bool> + Eval<Ast, A> +
} }
impl<S, A> Dsl<S> for Align<A> where S: Eval<Option<Ast>, A> { impl<S, A> Dsl<S> for Align<A> where S: Eval<Option<Ast>, A> {
fn try_provide (state: &S, source: Ast) -> Perhaps<Self> { fn try_provide (state: &S, source: impl DslValue) -> Perhaps<Self> {
if let Exp(_, source) = source.0 { if let Exp(_, source) = source.value() {
let mut rest = source.clone(); let mut rest = source.clone();
return Ok(Some(match rest.next().as_ref().and_then(|x|x.key()) { return Ok(Some(match rest.next().as_ref().and_then(|x|x.key()) {
Some("align/c") => Self::c(state.eval(rest.next(), ||"align/c: expected content")?), Some("align/c") => Self::c(state.eval(rest.next(), ||"align/c: expected content")?),
@ -52,8 +54,8 @@ impl<S, A> Dsl<S> for Align<A> where S: Eval<Option<Ast>, A> {
} }
impl<S, A, B> Dsl<S> for Bsp<A, B> where S: Eval<Option<Ast>, A> + Eval<Option<Ast>, B> { impl<S, A, B> Dsl<S> for Bsp<A, B> where S: Eval<Option<Ast>, A> + Eval<Option<Ast>, B> {
fn try_provide (state: &S, source: Ast) -> Perhaps<Self> { fn try_provide (state: &S, source: impl DslValue) -> Perhaps<Self> {
if let Exp(_, exp) = source.0 { if let Exp(_, exp) = source.value() {
let mut rest = exp.clone(); let mut rest = exp.clone();
return Ok(Some(match rest.next().as_ref().and_then(|x|x.key()) { return Ok(Some(match rest.next().as_ref().and_then(|x|x.key()) {
Some("bsp/n") => Self::n( Some("bsp/n") => Self::n(

View file

@ -64,7 +64,9 @@ impl ToTokens for ViewDef {
/// which might correspond to a given [TokenStream], /// which might correspond to a given [TokenStream],
/// while taking [#self_ty]'s state into consideration. /// while taking [#self_ty]'s state into consideration.
impl<'state> ::tengri::dsl::Dsl<Box<dyn Render<#output> + 'state>> for #self_ty { impl<'state> ::tengri::dsl::Dsl<Box<dyn Render<#output> + 'state>> for #self_ty {
fn try_provide (state: &'state #self_ty, mut words: ::tengri::dsl::Ast) -> Perhaps<Box<dyn Render<#output> + 'state>> { fn try_provide (state: &'state #self_ty, mut words: ::tengri::dsl::Ast) ->
Perhaps<Box<dyn Render<#output> + 'state>>
{
Ok(if let Some(::tengri::dsl::Token { value, .. }) = words.peek() { Ok(if let Some(::tengri::dsl::Token { value, .. }) = words.peek() {
match value { #(#builtin)* #(#exposed)* _ => None } match value { #(#builtin)* #(#exposed)* _ => None }
} else { } else {

View file

@ -1,14 +1,12 @@
use crate::*; use crate::*;
#[test] fn test_subcommand () -> Usually<()> { #[test] fn test_subcommand () -> Usually<()> {
use crate::input::{Command, Handle, handle};
//use crate::dsl::TokenIter;
use crate::tui::TuiIn; use crate::tui::TuiIn;
use crossterm::event::{Event, KeyEvent, KeyCode, KeyModifiers, KeyEventKind, KeyEventState}; use crossterm::event::{Event, KeyEvent, KeyCode, KeyModifiers, KeyEventKind, KeyEventState};
//use crate::input::*; use crate::input::*;
//use crate::dsl::*; use crate::dsl::*;
struct Test { struct Test {
//keys: InputMap<'static, Test, TestCommand, TuiIn, TokenIter<'static>> keys: InputLayers
} }
handle!(TuiIn: |self: Test, input|if let Some(command) = self.keys.command(self, input) { handle!(TuiIn: |self: Test, input|if let Some(command) = self.keys.command(self, input) {
Ok(Some(true)) Ok(Some(true))
@ -37,12 +35,12 @@ use crate::*;
} }
} }
let mut test = Test { let mut test = Test {
keys: InputMap::new(" keys: InputLayers::new(SourceIter::new("
(@a do-thing) (@a do-thing)
(@b do-thing-arg 0) (@b do-thing-arg 0)
(@c do-sub do-other-thing) (@c do-sub do-other-thing)
(@d do-sub do-other-thing-arg 0) (@d do-sub do-other-thing-arg 0)
".into()) ".into()))
}; };
assert_eq!(Some(true), test.handle(&TuiIn(Default::default(), Event::Key(KeyEvent { assert_eq!(Some(true), test.handle(&TuiIn(Default::default(), Event::Key(KeyEvent {
kind: KeyEventKind::Press, kind: KeyEventKind::Press,