diff options
| author | tslil <tslil@posteo.de> | 2026-08-24 20:39:57 +0100 |
|---|---|---|
| committer | tslil <tslil@posteo.de> | 2026-08-24 21:39:25 +0100 |
| commit | f3acb5cc0faf739f09112607bb98b6594184bd8b (patch) | |
| tree | 9f60aeda21a775f9d34ed8a70fffd9be638f27ce /rprt-engine/src/selection_functions/types.rs | |
| parent | 72a5e0fc968e2bea9f7d348c7189ebc15a5b15d3 (diff) | |
Basically Rust will fight you all the way if you treat it like Haskell, instead work with traits and impls on ZSTs.
Diffstat (limited to 'rprt-engine/src/selection_functions/types.rs')
| -rw-r--r-- | rprt-engine/src/selection_functions/types.rs | 237 |
1 files changed, 174 insertions, 63 deletions
diff --git a/rprt-engine/src/selection_functions/types.rs b/rprt-engine/src/selection_functions/types.rs index 6bd94bf..4cd76d3 100644 --- a/rprt-engine/src/selection_functions/types.rs +++ b/rprt-engine/src/selection_functions/types.rs @@ -1,13 +1,13 @@ use crate::{ buffer::{Buffer, BufferID}, expression::BuiltinSelectionFn, - selection::{Selection, VectoriseError}, + selection::{Interval, Selection, Vectorisable}, state::EditorState, }; use thiserror::Error; #[derive(Error, Debug)] -pub enum SFError { +pub enum SelectionFunctionError { #[error("Could not find buffer {0}")] BufferNotFound(BufferID), #[error("Function {0} has no dyadic form")] @@ -20,75 +20,186 @@ pub enum SFError { NotImplemented(BuiltinSelectionFn), } -pub enum SFArguments<'a> { - Niladic { - es: &'a EditorState, - }, - Monadic { - es: &'a EditorState, - left: Selection, - }, - Dyadic { - es: &'a EditorState, - left: Selection, - right: Selection, - }, -} +/// A selection function parameterised by `P` (the type of its parameter, +/// e.g. `usize` for character offsets). +/// +/// Each concrete function is a zero-sized struct implementing this trait; +/// the parameter value is passed at call time. The arity of the call +/// (niladic/monadic/dyadic) is routed by [`SelectionFunction::apply`]. +pub trait SelectionFunction<P>: Vectorisable<P, SelectionFunctionError> { + /// Name of the function, used in error messages. + const NAME: &'static str; + + /// The monadic form applied to a rank-0 (scalar) element. + fn monadic_rank0( + &self, + param: &P, + es: &EditorState, + buffer_id: BufferID, + pos: usize, + ) -> Result<Selection, SelectionFunctionError>; + + /// The monadic form applied to a rank-1 (interval) element. + fn monadic_rank1( + &self, + param: &P, + es: &EditorState, + buffer_id: BufferID, + interval: &Interval, + ) -> Result<Selection, SelectionFunctionError>; + + /// The niladic form. + fn niladic(&self, _param: &P, _es: &EditorState) -> Result<Selection, SelectionFunctionError> { + Err(SelectionFunctionError::NoNiladicForm(Self::NAME)) + } + + /// The monadic form: vectorise over `left`. + fn monadic( + &self, + param: &P, + es: &EditorState, + left: &Selection, + ) -> Result<Selection, SelectionFunctionError> { + self.vectorise(param, es, left) + } + + /// The dyadic form. + fn dyadic( + &self, + _param: &P, + _es: &EditorState, + _left: &Selection, + _right: &Selection, + ) -> Result<Selection, SelectionFunctionError> { + Err(SelectionFunctionError::NoDyadicForm(Self::NAME)) + } -pub type SFResult = Result<Selection, VectoriseError<SFError>>; - -#[macro_export] -macro_rules! selection_function { - ( - name: $name:ident, - param_type: $P:ty, - niladic: $niladic_body:expr, - monadic_rank0: $monadic_rank0_body:expr, - monadic_rank1: $monadic_rank1_body:expr - ) => { - fn $name(param: $P, arg: &SFArguments) -> SFResult { - match arg { - SFArguments::Niladic { es } => ($niladic_body)((param, es)), - SFArguments::Monadic { es, left } => { - left.vectorise(&(param, es), $monadic_rank0_body, $monadic_rank1_body) - } - SFArguments::Dyadic { .. } => Err(SFError::NoDyadicForm(stringify!($name)).into()), - } + /// Route to the arity form matching the selections supplied. + fn apply( + &self, + param: &P, + es: &EditorState, + left: Option<&Selection>, + right: Option<&Selection>, + ) -> Result<Selection, SelectionFunctionError> { + match (left, right) { + (None, None) => self.niladic(param, es), + (Some(left), None) => self.monadic(param, es, left), + (Some(left), Some(right)) => self.dyadic(param, es, left, right), + (None, Some(_)) => Err(SelectionFunctionError::NoLeftArgument), } - }; + } } -#[macro_export] -macro_rules! dispatch_selection_function { - ( - name: $name:ident, - param_type: $P:ty, - base: $bs:ident, - sequential: $sq:ident, - reverse: $rv:ident, - sequential_reverse: $sr:ident, - reverse_sequential: $rs:ident, - ) => { - pub fn $name( - param: $P, - arg: &SFArguments, - search_mod: Option<crate::expression::SearchModifier>, - ) -> SFResult { - match search_mod { - None => ($bs)(param, arg), - Some(crate::expression::SearchModifier::Sequential) => ($sq)(param, arg), - Some(crate::expression::SearchModifier::Reverse) => ($rv)(param, arg), - Some(crate::expression::SearchModifier::SequentialReverse) => ($sr)(param, arg), - Some(crate::expression::SearchModifier::ReverseSequential) => ($rs)(param, arg), - } - } - }; +/// Every selection function can vectorise a selection by applying its +/// monadic rank-0/rank-1 forms to each element. +impl<P, F: SelectionFunction<P> + ?Sized> Vectorisable<P, SelectionFunctionError> for F { + fn rank0( + &self, + param: &P, + es: &EditorState, + buffer_id: BufferID, + pos: usize, + ) -> Result<Selection, SelectionFunctionError> { + self.monadic_rank0(param, es, buffer_id, pos) + } + + fn rank1( + &self, + param: &P, + es: &EditorState, + buffer_id: BufferID, + interval: &Interval, + ) -> Result<Selection, SelectionFunctionError> { + self.monadic_rank1(param, es, buffer_id, interval) + } +} + +/// A selection function with no parameter. +/// +/// Blanket-implements [`SelectionFunction`] with `P = ()`, so +/// unparameterised functions are callable through the same interface. +pub trait UnparameterisedSelectionFunction { + /// Name of the function, used in error messages. + const NAME: &'static str; + + /// The monadic form applied to a rank-0 (scalar) element. + fn monadic_rank0( + &self, + es: &EditorState, + buffer_id: BufferID, + pos: usize, + ) -> Result<Selection, SelectionFunctionError>; + + /// The monadic form applied to a rank-1 (interval) element. + fn monadic_rank1( + &self, + es: &EditorState, + buffer_id: BufferID, + interval: &Interval, + ) -> Result<Selection, SelectionFunctionError>; + + /// The niladic form. + fn niladic(&self, _es: &EditorState) -> Result<Selection, SelectionFunctionError> { + Err(SelectionFunctionError::NoNiladicForm(Self::NAME)) + } + + /// The dyadic form. + fn dyadic( + &self, + _es: &EditorState, + _left: &Selection, + _right: &Selection, + ) -> Result<Selection, SelectionFunctionError> { + Err(SelectionFunctionError::NoDyadicForm(Self::NAME)) + } +} + +impl<F: UnparameterisedSelectionFunction + ?Sized> SelectionFunction<()> for F { + const NAME: &'static str = <F as UnparameterisedSelectionFunction>::NAME; + + fn monadic_rank0( + &self, + _param: &(), + es: &EditorState, + buffer_id: BufferID, + pos: usize, + ) -> Result<Selection, SelectionFunctionError> { + UnparameterisedSelectionFunction::monadic_rank0(self, es, buffer_id, pos) + } + + fn monadic_rank1( + &self, + _param: &(), + es: &EditorState, + buffer_id: BufferID, + interval: &Interval, + ) -> Result<Selection, SelectionFunctionError> { + UnparameterisedSelectionFunction::monadic_rank1(self, es, buffer_id, interval) + } + + fn niladic(&self, _param: &(), es: &EditorState) -> Result<Selection, SelectionFunctionError> { + UnparameterisedSelectionFunction::niladic(self, es) + } + + fn dyadic( + &self, + _param: &(), + es: &EditorState, + left: &Selection, + right: &Selection, + ) -> Result<Selection, SelectionFunctionError> { + UnparameterisedSelectionFunction::dyadic(self, es, left, right) + } } -pub fn get_buffer(es: &EditorState, buffer_id: BufferID) -> Result<&Buffer, SFError> { +pub fn get_buffer( + es: &EditorState, + buffer_id: BufferID, +) -> Result<&Buffer, SelectionFunctionError> { if let Some(buf) = es.get_buffer(buffer_id) { Ok(buf) } else { - Err(SFError::BufferNotFound(es.current_buffer_id)) + Err(SelectionFunctionError::BufferNotFound(es.current_buffer_id)) } } |
