From f3acb5cc0faf739f09112607bb98b6594184bd8b Mon Sep 17 00:00:00 2001 From: tslil Date: Mon, 24 Aug 2026 20:39:57 +0100 Subject: The big transposition-ing: move from functions as data to data of functions Basically Rust will fight you all the way if you treat it like Haskell, instead work with traits and impls on ZSTs. --- rprt-engine/src/evaluate.rs | 10 +- rprt-engine/src/selection.rs | 138 +++++----- .../src/selection_functions/function_character.rs | 298 ++++++++++++++++----- .../src/selection_functions/function_empty.rs | 175 +++++++++++- .../src/selection_functions/function_end.rs | 204 ++++++++++---- rprt-engine/src/selection_functions/mod.rs | 31 +-- .../selection_functions/result_transformation.rs | 47 ++-- rprt-engine/src/selection_functions/types.rs | 237 +++++++++++----- 8 files changed, 841 insertions(+), 299 deletions(-) (limited to 'rprt-engine/src') diff --git a/rprt-engine/src/evaluate.rs b/rprt-engine/src/evaluate.rs index 4f567fb..eeb1415 100644 --- a/rprt-engine/src/evaluate.rs +++ b/rprt-engine/src/evaluate.rs @@ -1,7 +1,7 @@ use crate::{ expression::{Composite, HookKind}, - selection::{Selection, SelectionError, VectoriseError}, - selection_functions::{evaluate_selection_function, SFError}, + selection::Selection, + selection_functions::{SelectionFunctionError, evaluate_selection_function}, state::{EditorState, GroupedChangeError, StateChange, StateResult}, text::Text, }; @@ -11,9 +11,7 @@ use thiserror::Error; #[derive(Error, Debug)] pub enum EvaluationError { #[error("{0}")] - SelectionFunctionError(VectoriseError), - #[error("{0}")] - SelectionUnionError(SelectionError), + SelectionFunctionError(SelectionFunctionError), #[error("Error in processing group {0}")] GroupError(GroupedChangeError), #[error("Invalid right-only application")] @@ -152,7 +150,7 @@ fn _eval( } .map_err(EvaluationError::GroupError)?; - let sel = Selection::union(selections).map_err(EvaluationError::SelectionUnionError)?; + let sel = Selection::union(selections); Ok((sel, states)) } } diff --git a/rprt-engine/src/selection.rs b/rprt-engine/src/selection.rs index a3259b8..45a895b 100644 --- a/rprt-engine/src/selection.rs +++ b/rprt-engine/src/selection.rs @@ -1,4 +1,5 @@ use crate::buffer::BufferID; +use crate::state::EditorState; use std::collections::HashMap; use std::collections::HashSet; use std::fmt::Display; @@ -69,26 +70,55 @@ pub enum Selection { }, } -// rust is ... sigh. I have to make a wrapper class and worse still _other -// people_ have to deal with my wrapper class! -#[derive(Error, Debug)] -pub enum VectoriseError -where - E: std::error::Error + 'static, -{ - #[error("An error occurred during processing: {0}")] - ProcessingError(E), - - #[error("A selection error occurred: {0}")] - SelectionError(SelectionError), -} +/// A type that knows how to act on the rank-0 (scalar) and rank-1 (interval) +/// elements of a [`Selection`], and so can be vectorised over one. +pub trait Vectorisable { + /// Action on a rank-0 element. + fn rank0( + &self, + param: &P, + es: &EditorState, + buffer_id: BufferID, + pos: usize, + ) -> Result; -impl From for VectoriseError -where - E: std::error::Error + 'static, -{ - fn from(err: E) -> Self { - VectoriseError::ProcessingError(err) + /// Action on a rank-1 element. + fn rank1( + &self, + param: &P, + es: &EditorState, + buffer_id: BufferID, + interval: &Interval, + ) -> Result; + + /// Vectorise `sel`: apply `rank0`/`rank1` to every element and union + /// the results. + fn vectorise(&self, param: &P, es: &EditorState, sel: &Selection) -> Result { + // Single rank-0/rank-1 elements pass through as-is (no rank + // promotion); multi-element selections are unioned. + match sel { + Selection::Scalar { buffer_id, pos } => Self::rank0(self, param, es, *buffer_id, *pos), + Selection::Vector { + buffer_id, + interval, + } => Self::rank1(self, param, es, *buffer_id, interval), + Selection::Vectors { buffer_id, ranges } => { + let mut selections = Vec::with_capacity(ranges.len()); + for interval in ranges { + selections.push(Self::rank1(self, param, es, *buffer_id, interval)?); + } + Ok(Selection::union(selections)) + } + Selection::MultiVectors { multi_ranges } => { + let mut selections = Vec::new(); + for (buffer_id, ranges) in multi_ranges { + for interval in ranges { + selections.push(Self::rank1(self, param, es, *buffer_id, interval)?); + } + } + Ok(Selection::union(selections)) + } + } } } @@ -210,15 +240,20 @@ impl Selection { } } - pub fn union(selections: Vec) -> Result { + /// Union the given selections into a single selection. + /// + /// The target rank is the maximum rank needed to hold all inputs (rank 3 + /// if they span multiple buffers), to which every input can be promoted, + /// so this operation cannot fail. + pub fn union(selections: Vec) -> Self { if selections.is_empty() { - return Ok(Self::empty()); + return Self::empty(); } let all_buffers: HashSet = selections.iter().flat_map(|s| s.buffers()).collect(); if all_buffers.is_empty() { - return Ok(Self::empty()); + return Self::empty(); } let max_rank = if all_buffers.len() > 1 { @@ -232,9 +267,13 @@ impl Selection { .max(Rank::Two) // At least rank 2 }; - let promoted: Result, SelectionError> = - selections.iter().map(|s| s.promote(max_rank)).collect(); - let promoted = promoted?; + let promoted: Vec = selections + .iter() + .map(|s| { + s.promote(max_rank) + .expect("union promotes to the max rank present, which cannot fail") + }) + .collect(); match max_rank { Rank::Two => { @@ -248,10 +287,10 @@ impl Selection { } } - Ok(Self::Vectors { + Self::Vectors { ranges: all_intervals, buffer_id, - }) + } } Rank::Three => { let mut buffer_intervals: HashMap> = @@ -273,52 +312,11 @@ impl Selection { } } - Ok(Self::MultiVectors { + Self::MultiVectors { multi_ranges: buffer_intervals, - }) + } } _ => unreachable!(), } } - - pub fn vectorise( - &self, - state: &S, - fn_rank_zero: impl Fn(&S, BufferID, usize) -> Result + 'static, - fn_rank_one: impl Fn(&S, BufferID, &Interval) -> Result + 'static, - ) -> Result> - where - E: std::error::Error + 'static, - { - // look at this mess! - use VectoriseError::{ProcessingError, SelectionError}; - let do_rank_one = |b: usize, rs: &Vec| { - rs.iter() - .map(|int| fn_rank_one(state, b, int).map_err(ProcessingError)) - .collect::>() - }; - - match self { - &Self::Scalar { buffer_id, pos } => { - fn_rank_zero(state, buffer_id, pos).map_err(ProcessingError) - } - Self::Vector { - interval, - buffer_id, - } => fn_rank_one(state, *buffer_id, interval).map_err(VectoriseError::ProcessingError), - Self::Vectors { buffer_id, ranges } => { - let results = do_rank_one(*buffer_id, ranges)?; - Self::union(results).map_err(SelectionError) - } - Self::MultiVectors { multi_ranges } => { - let all_ok: Vec> = multi_ranges - .iter() - .map(|(buffer_id, ranges)| do_rank_one(*buffer_id, ranges)) - .collect::>()?; - // It would seem that Rust has no built in monadic flatten, or in general cannot lift things to operate on Result... :( - let results: Vec = all_ok.into_iter().flatten().collect(); - Self::union(results).map_err(SelectionError) - } - } - } } diff --git a/rprt-engine/src/selection_functions/function_character.rs b/rprt-engine/src/selection_functions/function_character.rs index b4bbf0f..4b41297 100644 --- a/rprt-engine/src/selection_functions/function_character.rs +++ b/rprt-engine/src/selection_functions/function_character.rs @@ -1,91 +1,249 @@ use crate::{ - dispatch_selection_function, - selection::Selection, - selection_function, - selection_functions::types::{SFArguments, SFError, SFResult, get_buffer}, + buffer::BufferID, + expression::SearchModifier, + selection::{Interval, Selection}, + selection_functions::types::{SelectionFunction, SelectionFunctionError, get_buffer}, state::EditorState, }; -selection_function! { - name: character, - param_type: usize, - niladic: |(param, es): (usize, &EditorState)| { +pub struct Character; + +impl SelectionFunction for Character { + const NAME: &'static str = "character"; + + fn niladic( + &self, + param: &usize, + es: &EditorState, + ) -> Result { let buf = get_buffer(es, es.current_buffer_id)?; Ok(Selection::Scalar { buffer_id: es.current_buffer_id, - pos: usize::min(param, buf.max_pos()) + pos: usize::min(*param, buf.max_pos()), + }) + } - }) }, - monadic_rank0: |_, buffer_id, pos| Ok(Selection::Scalar {buffer_id, pos}), - monadic_rank1: |(param, _), buffer_id, int| Ok( - Selection::Scalar {buffer_id, pos: usize::min(int.end, *param)} - ) + fn monadic_rank0( + &self, + _param: &usize, + _es: &EditorState, + buffer_id: BufferID, + pos: usize, + ) -> Result { + Ok(Selection::Scalar { buffer_id, pos }) + } + + fn monadic_rank1( + &self, + param: &usize, + _es: &EditorState, + buffer_id: BufferID, + interval: &Interval, + ) -> Result { + Ok(Selection::Scalar { + buffer_id, + pos: usize::min(interval.start + *param, interval.end), + }) + } } -selection_function!( - name: sequential_character, - param_type: usize, - niladic: |_| Err(SFError::NoNiladicForm(";n").into()), - monadic_rank0: |(param, es), buffer_id, pos| { +pub struct SequentialCharacter; + +impl SelectionFunction for SequentialCharacter { + const NAME: &'static str = "sequential_character"; + + fn niladic( + &self, + _param: &usize, + _es: &EditorState, + ) -> Result { + Err(SelectionFunctionError::NoNiladicForm(";n")) + } + + fn monadic_rank0( + &self, + param: &usize, + es: &EditorState, + buffer_id: BufferID, + pos: usize, + ) -> Result { let buf = get_buffer(es, buffer_id)?; - Ok(Selection::Scalar {buffer_id, pos: usize::min(pos+*param, buf.max_pos())}) - }, - monadic_rank1: |(param, es), buffer_id, int| { + Ok(Selection::Scalar { + buffer_id, + pos: usize::min(pos + *param, buf.max_pos()), + }) + } + + fn monadic_rank1( + &self, + param: &usize, + es: &EditorState, + buffer_id: BufferID, + interval: &Interval, + ) -> Result { let buf = get_buffer(es, buffer_id)?; - Ok(Selection::Scalar {buffer_id, pos: usize::min(int.end+*param, buf.max_pos())}) + Ok(Selection::Scalar { + buffer_id, + pos: usize::min(interval.end + *param, buf.max_pos()), + }) } -); +} + +pub struct ReverseCharacter; + +impl SelectionFunction for ReverseCharacter { + const NAME: &'static str = "reverse_character"; -selection_function!( - name: reverse_character, - param_type: usize, - niladic: |(param, es): (usize, &EditorState)| { + fn niladic( + &self, + param: &usize, + es: &EditorState, + ) -> Result { let buf = get_buffer(es, es.current_buffer_id)?; Ok(Selection::Scalar { buffer_id: es.current_buffer_id, - pos: if buf.max_pos() < param { + pos: if buf.max_pos() < *param { + 0 + } else { + buf.max_pos() - *param + }, + }) + } + + fn monadic_rank0( + &self, + _param: &usize, + _es: &EditorState, + buffer_id: BufferID, + pos: usize, + ) -> Result { + Ok(Selection::Scalar { buffer_id, pos }) + } + + fn monadic_rank1( + &self, + param: &usize, + _es: &EditorState, + buffer_id: BufferID, + interval: &Interval, + ) -> Result { + Ok(Selection::Scalar { + buffer_id, + pos: if *param < interval.end { + interval.end - *param + } else { + 0 + }, + }) + } +} + +pub struct SequentialReverseCharacter; + +impl SelectionFunction for SequentialReverseCharacter { + const NAME: &'static str = "sequential_reverse_character"; + + fn niladic( + &self, + _param: &usize, + _es: &EditorState, + ) -> Result { + Err(SelectionFunctionError::NoNiladicForm(";'n")) + } + + fn monadic_rank0( + &self, + param: &usize, + _es: &EditorState, + buffer_id: BufferID, + pos: usize, + ) -> Result { + Ok(Selection::Scalar { + buffer_id, + pos: if *param < pos { pos - *param } else { 0 }, + }) + } + + fn monadic_rank1( + &self, + param: &usize, + _es: &EditorState, + buffer_id: BufferID, + interval: &Interval, + ) -> Result { + Ok(Selection::Scalar { + buffer_id, + pos: if *param < interval.end { + interval.end - *param + } else { 0 + }, + }) + } +} + +pub struct ReverseSequentialCharacter; + +impl SelectionFunction for ReverseSequentialCharacter { + const NAME: &'static str = "reverse_sequential_character"; + + fn niladic( + &self, + _param: &usize, + _es: &EditorState, + ) -> Result { + Err(SelectionFunctionError::NoNiladicForm("';n")) + } + + fn monadic_rank0( + &self, + param: &usize, + _es: &EditorState, + buffer_id: BufferID, + pos: usize, + ) -> Result { + Ok(Selection::Scalar { + buffer_id, + pos: if *param < pos { pos - *param } else { 0 }, + }) + } + + fn monadic_rank1( + &self, + param: &usize, + _es: &EditorState, + buffer_id: BufferID, + interval: &Interval, + ) -> Result { + Ok(Selection::Scalar { + buffer_id, + pos: if *param < interval.start { + interval.start - *param } else { - buf.max_pos() - param + 0 }, }) - }, - monadic_rank0: |_, buffer_id, pos| Ok(Selection::Scalar {buffer_id, pos}), - monadic_rank1: |(param, _), buffer_id, int| Ok( - Selection::Scalar {buffer_id, pos: usize::min(int.end, *param)} - ) -); - -selection_function!( - name: sequential_reverse_character, - param_type: usize, - niladic: |_| Err(SFError::NoNiladicForm(";'n").into()), - monadic_rank0: |(param, _), buffer_id, pos| { - Ok(Selection::Scalar {buffer_id, pos: if *param, + right: Option<&Selection>, + search_mod: Option, +) -> Result { + match search_mod { + None => Character.apply(&offset, es, left, right), + Some(SearchModifier::Sequential) => SequentialCharacter.apply(&offset, es, left, right), + Some(SearchModifier::Reverse) => ReverseCharacter.apply(&offset, es, left, right), + Some(SearchModifier::SequentialReverse) => { + SequentialReverseCharacter.apply(&offset, es, left, right) + } + Some(SearchModifier::ReverseSequential) => { + ReverseSequentialCharacter.apply(&offset, es, left, right) + } + } +} diff --git a/rprt-engine/src/selection_functions/function_empty.rs b/rprt-engine/src/selection_functions/function_empty.rs index bf0a45c..4b69dcf 100644 --- a/rprt-engine/src/selection_functions/function_empty.rs +++ b/rprt-engine/src/selection_functions/function_empty.rs @@ -1,12 +1,175 @@ use crate::{ - selection::Selection, - selection_functions::types::{SFArguments, SFResult}, + buffer::BufferID, + expression::SearchModifier, + selection::{Interval, Selection}, + selection_functions::types::{ + SelectionFunction, SelectionFunctionError, UnparameterisedSelectionFunction, + }, + state::EditorState, }; -pub fn empty_sequential(_: SFArguments) -> SFResult { - Ok(Selection::empty()) +pub struct Empty; + +impl UnparameterisedSelectionFunction for Empty { + const NAME: &'static str = "empty"; + + fn niladic(&self, _es: &EditorState) -> Result { + Ok(Selection::empty()) + } + + fn monadic_rank0( + &self, + _es: &EditorState, + _buffer_id: BufferID, + _pos: usize, + ) -> Result { + Ok(Selection::empty()) + } + + fn monadic_rank1( + &self, + _es: &EditorState, + _buffer_id: BufferID, + _interval: &Interval, + ) -> Result { + Ok(Selection::empty()) + } +} + +pub struct ReverseEmpty; + +impl UnparameterisedSelectionFunction for ReverseEmpty { + const NAME: &'static str = "reverse_empty"; + + fn niladic(&self, es: &EditorState) -> Result { + Ok(empty_in_buffer(es.current_buffer_id)) + } + + fn monadic_rank0( + &self, + _es: &EditorState, + buffer_id: BufferID, + _pos: usize, + ) -> Result { + Ok(empty_in_buffer(buffer_id)) + } + + fn monadic_rank1( + &self, + _es: &EditorState, + buffer_id: BufferID, + _interval: &Interval, + ) -> Result { + Ok(empty_in_buffer(buffer_id)) + } +} + +pub struct SequentialEmpty; + +impl UnparameterisedSelectionFunction for SequentialEmpty { + const NAME: &'static str = "sequential_empty"; + + fn niladic(&self, _es: &EditorState) -> Result { + Err(SelectionFunctionError::NoNiladicForm(";e")) + } + + fn monadic_rank0( + &self, + _es: &EditorState, + buffer_id: BufferID, + _pos: usize, + ) -> Result { + Ok(empty_in_buffer(buffer_id)) + } + + fn monadic_rank1( + &self, + _es: &EditorState, + buffer_id: BufferID, + _interval: &Interval, + ) -> Result { + Ok(empty_in_buffer(buffer_id)) + } +} + +pub struct SequentialReverseEmpty; + +impl UnparameterisedSelectionFunction for SequentialReverseEmpty { + const NAME: &'static str = "sequential_reverse_empty"; + + fn niladic(&self, _es: &EditorState) -> Result { + Err(SelectionFunctionError::NoNiladicForm(";'e")) + } + + fn monadic_rank0( + &self, + _es: &EditorState, + buffer_id: BufferID, + _pos: usize, + ) -> Result { + Ok(empty_in_buffer(buffer_id)) + } + + fn monadic_rank1( + &self, + _es: &EditorState, + buffer_id: BufferID, + _interval: &Interval, + ) -> Result { + Ok(empty_in_buffer(buffer_id)) + } +} + +pub struct ReverseSequentialEmpty; + +impl UnparameterisedSelectionFunction for ReverseSequentialEmpty { + const NAME: &'static str = "reverse_sequential_empty"; + + fn niladic(&self, _es: &EditorState) -> Result { + Err(SelectionFunctionError::NoNiladicForm("';e")) + } + + fn monadic_rank0( + &self, + _es: &EditorState, + buffer_id: BufferID, + _pos: usize, + ) -> Result { + Ok(empty_in_buffer(buffer_id)) + } + + fn monadic_rank1( + &self, + _es: &EditorState, + buffer_id: BufferID, + _interval: &Interval, + ) -> Result { + Ok(empty_in_buffer(buffer_id)) + } +} + +fn empty_in_buffer(buffer_id: BufferID) -> Selection { + Selection::Vectors { + buffer_id, + ranges: Vec::new(), + } } -pub fn empty_structural(_: SFArguments) -> SFResult { - Ok(Selection::empty()) +pub fn selection_function_empty( + es: &EditorState, + left: Option<&Selection>, + right: Option<&Selection>, + search_mod: Option, +) -> Result { + match search_mod { + None => Empty.apply(&(), es, left, right), + Some(SearchModifier::Sequential) => SequentialEmpty.apply(&(), es, left, right), + Some(SearchModifier::Reverse) => ReverseEmpty.apply(&(), es, left, right), + Some(SearchModifier::SequentialReverse) => { + SequentialReverseEmpty.apply(&(), es, left, right) + } + Some(SearchModifier::ReverseSequential) => { + ReverseSequentialEmpty.apply(&(), es, left, right) + } + } } diff --git a/rprt-engine/src/selection_functions/function_end.rs b/rprt-engine/src/selection_functions/function_end.rs index 75b4c96..ad161d2 100644 --- a/rprt-engine/src/selection_functions/function_end.rs +++ b/rprt-engine/src/selection_functions/function_end.rs @@ -1,57 +1,142 @@ use crate::{ - dispatch_selection_function, - selection::Selection, - selection_function, - selection_functions::types::{SFArguments, SFError, SFResult, get_buffer}, + buffer::BufferID, + expression::SearchModifier, + selection::{Interval, Selection}, + selection_functions::types::{ + SelectionFunction, SelectionFunctionError, UnparameterisedSelectionFunction, get_buffer, + }, state::EditorState, }; -selection_function! { - name: end, - param_type: (), - niladic: |(_, es): ((), &EditorState)| { +pub struct End; + +impl UnparameterisedSelectionFunction for End { + const NAME: &'static str = "end"; + + fn niladic(&self, es: &EditorState) -> Result { let buf = get_buffer(es, es.current_buffer_id)?; Ok(Selection::Scalar { buffer_id: es.current_buffer_id, pos: buf.max_pos(), }) - }, - monadic_rank0: |_, buffer_id, pos | Ok(Selection::Scalar { buffer_id, pos }), - monadic_rank1: |_, buffer_id, int | Ok(Selection::Scalar { buffer_id, pos: int.end }) + } + + fn monadic_rank0( + &self, + _es: &EditorState, + buffer_id: BufferID, + pos: usize, + ) -> Result { + Ok(Selection::Scalar { buffer_id, pos }) + } + + fn monadic_rank1( + &self, + _es: &EditorState, + buffer_id: BufferID, + interval: &Interval, + ) -> Result { + Ok(Selection::Scalar { + buffer_id, + pos: interval.end, + }) + } } -selection_function! { - name: reverse_end, - param_type: (), - niladic: |(_, es): ((), &EditorState)| { +pub struct ReverseEnd; + +impl UnparameterisedSelectionFunction for ReverseEnd { + const NAME: &'static str = "reverse_end"; + + fn niladic(&self, es: &EditorState) -> Result { Ok(Selection::Scalar { buffer_id: es.current_buffer_id, pos: 0, }) - }, - monadic_rank0: |_, buffer_id, pos | Ok(Selection::Scalar { buffer_id, pos }), - monadic_rank1: |_, buffer_id, int | Ok(Selection::Scalar { buffer_id, pos: int.start }) + } + + fn monadic_rank0( + &self, + _es: &EditorState, + buffer_id: BufferID, + pos: usize, + ) -> Result { + Ok(Selection::Scalar { buffer_id, pos }) + } + + fn monadic_rank1( + &self, + _es: &EditorState, + buffer_id: BufferID, + interval: &Interval, + ) -> Result { + Ok(Selection::Scalar { + buffer_id, + pos: interval.start, + }) + } } -selection_function! { - name: sequential_end, - param_type: (), - niladic: |_| Err(SFError::NoNiladicForm(";$").into()), - monadic_rank0: |_, buffer_id, pos| Ok(Selection::Scalar { buffer_id, pos }), - monadic_rank1: |(_, es), buffer_id, _| { +pub struct SequentialEnd; + +impl UnparameterisedSelectionFunction for SequentialEnd { + const NAME: &'static str = "sequential_end"; + + fn niladic(&self, _es: &EditorState) -> Result { + Err(SelectionFunctionError::NoNiladicForm(";$")) + } + + fn monadic_rank0( + &self, + es: &EditorState, + buffer_id: BufferID, + _pos: usize, + ) -> Result { + let buf = get_buffer(es, buffer_id)?; + Ok(Selection::Scalar { + buffer_id, + pos: buf.max_pos(), + }) + } + + fn monadic_rank1( + &self, + es: &EditorState, + buffer_id: BufferID, + _interval: &Interval, + ) -> Result { let buf = get_buffer(es, buffer_id)?; - Ok(Selection::Scalar { buffer_id, pos: buf.max_pos() }) + Ok(Selection::Scalar { + buffer_id, + pos: buf.max_pos(), + }) } } -selection_function! { - name: sequential_reverse_end, - param_type: (), - niladic: |_| Err(SFError::NoNiladicForm(";'$").into()), - monadic_rank0: |_, buffer_id, pos| { +pub struct SequentialReverseEnd; + +impl UnparameterisedSelectionFunction for SequentialReverseEnd { + const NAME: &'static str = "sequential_reverse_end"; + + fn niladic(&self, _es: &EditorState) -> Result { + Err(SelectionFunctionError::NoNiladicForm(";'$")) + } + + fn monadic_rank0( + &self, + _es: &EditorState, + buffer_id: BufferID, + pos: usize, + ) -> Result { Ok(Selection::Scalar { buffer_id, pos }) - }, - monadic_rank1: |_, buffer_id, interval| { + } + + fn monadic_rank1( + &self, + _es: &EditorState, + buffer_id: BufferID, + interval: &Interval, + ) -> Result { Ok(Selection::Scalar { buffer_id, pos: interval.end, @@ -59,14 +144,30 @@ selection_function! { } } -selection_function! { - name: reverse_sequential_end, - param_type: (), - niladic: |_| Err(SFError::NoNiladicForm("';$").into()), - monadic_rank0: |_, buffer_id, pos| { +pub struct ReverseSequentialEnd; + +impl UnparameterisedSelectionFunction for ReverseSequentialEnd { + const NAME: &'static str = "reverse_sequential_end"; + + fn niladic(&self, _es: &EditorState) -> Result { + Err(SelectionFunctionError::NoNiladicForm("';$")) + } + + fn monadic_rank0( + &self, + _es: &EditorState, + buffer_id: BufferID, + pos: usize, + ) -> Result { Ok(Selection::Scalar { buffer_id, pos }) - }, - monadic_rank1: |_, buffer_id, interval| { + } + + fn monadic_rank1( + &self, + _es: &EditorState, + buffer_id: BufferID, + interval: &Interval, + ) -> Result { Ok(Selection::Scalar { buffer_id, pos: interval.end, @@ -74,12 +175,19 @@ selection_function! { } } -dispatch_selection_function!( - name: selection_function_end, - param_type: (), - base: end, - sequential: sequential_end, - reverse: reverse_end, - sequential_reverse: sequential_reverse_end, - reverse_sequential: reverse_sequential_end, -); +/// Dispatch on the search modifier and apply the matching end-family +/// function to the supplied selections. +pub fn selection_function_end( + es: &EditorState, + left: Option<&Selection>, + right: Option<&Selection>, + search_mod: Option, +) -> Result { + match search_mod { + None => End.apply(&(), es, left, right), + Some(SearchModifier::Sequential) => SequentialEnd.apply(&(), es, left, right), + Some(SearchModifier::Reverse) => ReverseEnd.apply(&(), es, left, right), + Some(SearchModifier::SequentialReverse) => SequentialReverseEnd.apply(&(), es, left, right), + Some(SearchModifier::ReverseSequential) => ReverseSequentialEnd.apply(&(), es, left, right), + } +} diff --git a/rprt-engine/src/selection_functions/mod.rs b/rprt-engine/src/selection_functions/mod.rs index b753200..85c333e 100644 --- a/rprt-engine/src/selection_functions/mod.rs +++ b/rprt-engine/src/selection_functions/mod.rs @@ -3,9 +3,9 @@ use crate::{ selection::Selection, selection_functions::{ function_character::selection_function_character, + function_empty::selection_function_empty, function_end::selection_function_end, result_transformation::{complement_transform, conditional_transform}, - types::SFArguments, }, state::EditorState, }; @@ -16,7 +16,9 @@ mod function_end; mod result_transformation; mod types; -pub use crate::selection_functions::types::{SFError, SFResult}; +pub use crate::selection_functions::types::{ + SelectionFunction, SelectionFunctionError, UnparameterisedSelectionFunction, +}; pub fn evaluate_selection_function( es: &EditorState, @@ -25,31 +27,30 @@ pub fn evaluate_selection_function( result_transform: Option, left: Option, right: Option, -) -> SFResult { - let sf_args = match (left, right) { - (None, None) => Ok(SFArguments::Niladic { es }), - (Some(left), None) => Ok(SFArguments::Monadic { es, left }), - (Some(left), Some(right)) => Ok(SFArguments::Dyadic { es, left, right }), - _ => Err(SFError::NoLeftArgument), - }?; +) -> Result { let res = match sf { BuiltinSelectionFn::CharOffset(off) => { - selection_function_character(off, &sf_args, search_mod) + selection_function_character(off, es, left.as_ref(), right.as_ref(), search_mod) + } + BuiltinSelectionFn::Empty => { + selection_function_empty(es, left.as_ref(), right.as_ref(), search_mod) + } + BuiltinSelectionFn::EndOfBuffer => { + selection_function_end(es, left.as_ref(), right.as_ref(), search_mod) } - BuiltinSelectionFn::EndOfBuffer => selection_function_end((), &sf_args, search_mod), - x => Err(SFError::NotImplemented(x).into()), + x => Err(SelectionFunctionError::NotImplemented(x)), }?; if let Some(result_transform) = result_transform { match result_transform { ResultTransform::Complement => complement_transform(es, res), - ResultTransform::Conditional => conditional_transform(res, &sf_args), + ResultTransform::Conditional => conditional_transform(res, left.as_ref()), ResultTransform::ConditionalComplement => { let res = complement_transform(es, res)?; - conditional_transform(res, &sf_args) + conditional_transform(res, left.as_ref()) } ResultTransform::ComplementConditional => { - let res = conditional_transform(res, &sf_args)?; + let res = conditional_transform(res, left.as_ref())?; complement_transform(es, res) } } diff --git a/rprt-engine/src/selection_functions/result_transformation.rs b/rprt-engine/src/selection_functions/result_transformation.rs index c150916..b437afd 100644 --- a/rprt-engine/src/selection_functions/result_transformation.rs +++ b/rprt-engine/src/selection_functions/result_transformation.rs @@ -1,16 +1,20 @@ use crate::{ buffer::BufferID, - selection::{Interval, Selection}, - selection_functions::types::{SFArguments, SFError, SFResult}, + selection::{Interval, Selection, Vectorisable}, + selection_functions::types::SelectionFunctionError, state::EditorState, }; -pub fn complement_transform(es: &EditorState, result: Selection) -> SFResult { - fn complement_rank0( +struct Complement; + +impl Vectorisable<(), SelectionFunctionError> for Complement { + fn rank0( + &self, + _param: &(), es: &EditorState, buffer_id: BufferID, pos: usize, - ) -> Result { + ) -> Result { if let Some(buf) = es.get_buffer(buffer_id) { let mut ranges = Vec::new(); if pos > 0 { @@ -22,15 +26,17 @@ pub fn complement_transform(es: &EditorState, result: Selection) -> SFResult { } Ok(Selection::Vectors { buffer_id, ranges }) } else { - Err(SFError::BufferNotFound(buffer_id)) + Err(SelectionFunctionError::BufferNotFound(buffer_id)) } } - fn complement_rank1( + fn rank1( + &self, + _param: &(), es: &EditorState, buffer_id: BufferID, interval: &Interval, - ) -> Result { + ) -> Result { if let Some(buf) = es.get_buffer(buffer_id) { let max_pos = buf.max_pos(); if interval.start == 0 && interval.end >= max_pos { @@ -50,25 +56,24 @@ pub fn complement_transform(es: &EditorState, result: Selection) -> SFResult { } Ok(Selection::Vectors { buffer_id, ranges }) } else { - Err(SFError::BufferNotFound(buffer_id)) + Err(SelectionFunctionError::BufferNotFound(buffer_id)) } } +} - result.vectorise(es, complement_rank0, complement_rank1) +pub fn complement_transform( + es: &EditorState, + result: Selection, +) -> Result { + Complement.vectorise(&(), es, &result) } -pub fn conditional_transform(mut result: Selection, arg: &SFArguments) -> SFResult { +pub fn conditional_transform( + mut result: Selection, + left: Option<&Selection>, +) -> Result { if !result.is_empty() { - Ok(match arg { - // TODO: think about this niladic case? - SFArguments::Niladic { .. } => Selection::empty(), - SFArguments::Monadic { es: _, left } => left.clone(), - SFArguments::Dyadic { - es: _, - left, - right: _, - } => left.clone(), - }) + Ok(left.cloned().unwrap_or_else(Selection::empty)) } else { Ok(result) } 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

: Vectorisable { + /// 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; + + /// The monadic form applied to a rank-1 (interval) element. + fn monadic_rank1( + &self, + param: &P, + es: &EditorState, + buffer_id: BufferID, + interval: &Interval, + ) -> Result; + + /// The niladic form. + fn niladic(&self, _param: &P, _es: &EditorState) -> Result { + Err(SelectionFunctionError::NoNiladicForm(Self::NAME)) + } + + /// The monadic form: vectorise over `left`. + fn monadic( + &self, + param: &P, + es: &EditorState, + left: &Selection, + ) -> Result { + self.vectorise(param, es, left) + } + + /// The dyadic form. + fn dyadic( + &self, + _param: &P, + _es: &EditorState, + _left: &Selection, + _right: &Selection, + ) -> Result { + Err(SelectionFunctionError::NoDyadicForm(Self::NAME)) + } -pub type SFResult = Result>; - -#[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 { + 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, - ) -> 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 + ?Sized> Vectorisable for F { + fn rank0( + &self, + param: &P, + es: &EditorState, + buffer_id: BufferID, + pos: usize, + ) -> Result { + self.monadic_rank0(param, es, buffer_id, pos) + } + + fn rank1( + &self, + param: &P, + es: &EditorState, + buffer_id: BufferID, + interval: &Interval, + ) -> Result { + 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; + + /// The monadic form applied to a rank-1 (interval) element. + fn monadic_rank1( + &self, + es: &EditorState, + buffer_id: BufferID, + interval: &Interval, + ) -> Result; + + /// The niladic form. + fn niladic(&self, _es: &EditorState) -> Result { + Err(SelectionFunctionError::NoNiladicForm(Self::NAME)) + } + + /// The dyadic form. + fn dyadic( + &self, + _es: &EditorState, + _left: &Selection, + _right: &Selection, + ) -> Result { + Err(SelectionFunctionError::NoDyadicForm(Self::NAME)) + } +} + +impl SelectionFunction<()> for F { + const NAME: &'static str = ::NAME; + + fn monadic_rank0( + &self, + _param: &(), + es: &EditorState, + buffer_id: BufferID, + pos: usize, + ) -> Result { + UnparameterisedSelectionFunction::monadic_rank0(self, es, buffer_id, pos) + } + + fn monadic_rank1( + &self, + _param: &(), + es: &EditorState, + buffer_id: BufferID, + interval: &Interval, + ) -> Result { + UnparameterisedSelectionFunction::monadic_rank1(self, es, buffer_id, interval) + } + + fn niladic(&self, _param: &(), es: &EditorState) -> Result { + UnparameterisedSelectionFunction::niladic(self, es) + } + + fn dyadic( + &self, + _param: &(), + es: &EditorState, + left: &Selection, + right: &Selection, + ) -> Result { + 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)) } } -- cgit v1.2.3