# Rank PolymoRphic Text editor (RPRT) A compositional, point-free language for text manipulation. # 1. Core Concepts ## 1.1 Two Fundamental Types All functions work with two core types: 1. **Selection** — addresses and regions in buffers Every selection exists within one or more buffers. Selections have a rank: - **Rank 0**: Position — a natural number offset within a single buffer - **Rank 1**: Range — a pair of positions [start,end) with capture groups, within a single buffer - **Rank 2**: Ranges — a list of ranges with capture groups, within a single buffer - **Rank 3**: Multi-buffer selection — a mapping from buffer identifiers to rank 2 selections Ranks 0-2 operate within a single buffer (typically the current buffer). Rank 3 is distinguished by spanning multiple buffers simultaneously. 2. **Text** — string data ### Function Types A **Selection function** is something of signature `Selection × Selection → Selection`. A **Text function** is something of signature `Selection × Text → Selection`. Following APL conventions, functions may have nullary, monadic, and dyadic forms. In these cases the number of arguments that they take varies (though not their types). ### [Leading axis theory](https://aplwiki.com/wiki/Leading_axis_theory) and broadcasting The leading axis is the buffer selection, followed by the range _index_, followed by the character position in each range. When broadcasting occurs axes are aligned. TODO `α F ω` explain ### Trains TODO ## 1.2 The Editor State Monad All RPRT expressions evaluate within an implicit `EditorState` monad containing: - Buffer contents (for all open buffers) - Current buffer - Current selection - File/buffer mappings Selection and Text functions have signatures `EditorState Selection` and `EditorState Text` respectively. ## 1.3 Type Coercion When a Text argument is required, a Selection is implicitly coerced to Text by extracting its contents. This coercion has no side effects. | Input rank | Coerced Text | |------------|------------------------------------------------------------------| | 0 | Empty string (position has no extent) | | 1 | Contents of the range | | 2 | Contents of each range (text function broadcasts per range) | | 3 | Contents per buffer per range (broadcasts per buffer then range) | For ranks 2 and 3, the selection provides multiple text values. ## 1.4 Evaluation Contexts **Top-level Selection:** Sets the editor's current selection. ``` α # Finds pattern AND sets editor selection to it ``` **All text operations:** Modify editor content. Note that `F` returns a selection, so at the top level this also changes the current selection. ``` α F τ # F modifies contents, sets selection to changed region. α F (β G τ) # Inner G modifies contents but doesn't set selection. F modifies contents and sets the new selection. ``` **Buffer switching statement:** The `b` statement changes the current buffer in the EditorState. ``` b "name" # Switch current buffer to "name" /pattern/ # Searches in "name" ``` The `b` statement is not a function and cannot be composed. It only appears as a top-level statement. ## 1.5 The Grouping Operator `{}` The grouping operator `{}` provides transactional semantics: all operations within see the same input editor state, and their modifications are merged with position tracking where possible. Any error aborts the entire evaluation. **Syntax:** `{ op₁ , op₂ , ... , opₙ }` **Semantics:** - Captures editor state on entry - All operations execute with the original positions - Returns the union of all resulting Selections # 1.6 Composition and Parsing **Precedence:** Following BQN rules: - Operators bind tighter than functions - Functions and operators apply left-to-right - Dyadic functions are infix # 2. Selection Functions Selection functions produce Selection outputs and take zero (nullary functions), one (monadic functions), or two (dyadic functions) inputs of type Selection. ## 2.1 Core Selection Functions There are ten core selection functions in RPRT. All ten have nullary and monadic forms, while the function `-` additionally has dyadic semantics---it is the only selection function that may take two arguments. | Function | Nullary semantics | Output rank | |------------|------------------------------------------------------------|-------------| | `e` | Empty selection | 2 | | `$` | End of buffer | 0 | | `#n` (n∈ℕ) | Character n in buffer | 0 | | `n` (n∈ℕ) | Line n in buffer | 1 | | `/re/` | First match forward in buffer from start | 1 | | `x/re/` | All matches within buffer | 2 | | `+n` | Line n in buffer | 1 | | `B/re/` | All buffers whose name match re, mapping to their contents | 3 | | `-` | Entire buffer (0-$) | 2 | In the monadic case, all defined functions are rank preserving. Their action on ranks > 1 are given by broadcasting, that is, `α F` where `α` has rank 2 is computed by applying `F` to each rank 1 range in `α` and collecting the results into a list of ranges, mutatis mutandis for rank 3. | Function | Semantics for rank 0 `α` | Semantics for rank 1 `α` | |-----------|----------------------------------|------------------------------------------| | `α e` | Empty selection (rank 2) | Empty selection (rank 2) | | `α $` | `α` | End position of `α` | | `α #n` | `α` | Char n within `α` | | `α n` | `α` | Line n within `α` | | `α /re/` | `α` | First re within `α` | | `α x/re/` | `α` | All re within `α` | | `α +n` | `α` | Line n within `α` | | `α B/re/` | Buffer matches `re`? `α` : empty | buffer matches `re`? `α` : empty | | `α -` | Range from `α` to end of buffer | Range from start of `α` to end of buffer | The span function `-` is the only selection function with dyadic semantics. It creates ranges by spanning from one selection to another. | Rank of `α` | Rank of `ω` | Rank of `α-ω` | Semantics | |-------------|-------------|---------------|-------------------------------------------------------| | 0 | 0 | 1 | Range from `α` to `ω` | | 0 | 1 | 1 | Range from `α` to end of `ω` | | 1 | 0 | 1 | Range from start of `α` to `ω` | | 1 | 1 | 1 | Range from start of `α` to end of `ω` | | 0/1 | 2 | 2 | `α` to each range in `ω` (broadcast) | | 2 | 0/1 | 2 | Each range in `α` to `ω` (broadcast) | | 2 | 2 | 2 | Pairwise ranges (zip shortest) | | 0 | 3 | 3 | `α` to each range in `ω` (broadcast) | | 3 | 0 | 3 | Each range in `α` to `ω` (broadcast) | | 3 | 1/2 | - | Erroneous (TODO: maybe makes sense if indices align?) | | 1/2 | 3 | - | Erroneous (TODO: maybe makes sense if indices align?) | ## 2.2 Selection Function Operators Operators take functions as arguments and return new functions. ### 2.2.1 The Reverse Operator `'` The reverse operator `'` takes a selection function and reverses its direction. **Note:** `'F` is not defined when `F` is one of the functions `B-` . | Function | Nullary | Rank | |----------|--------------------------------|------| | `'e` | synonymous with `e` | 2 | | `'$` | Start of buffer (position 0) | 0 | | `'#n` | Character n from end of buffer | 0 | | `'n` | Line n from end of buffer | 1 | | `'/re/` | First match backward from end | 1 | | `'x/re/` | All matches, right-to-left | 2 | | `'+n` | Line -n in buffer | 1 | In the monadic case, all functions are rank preserving, and all of the above functions are the identity on rank 0 inputs. Their action on ranks > 1 is by broadcasting. | Function | Semantics for rank 0 `α` | Semantics for rank 1 `α` | |------------|--------------------------|----------------------------------| | `α 'e` | Empty selection (rank 2) | Empty selection (rank 2) | | `α '$` | `α` | Start position of `α` | | `α '#n` | `α` | Char n from end of `α` | | `α 'n` | `α` | Line n from end of `α` | | `α '/re/` | `α` | First match backward within `α` | | `α 'x/re/` | `α` | All matches right-to-left in `α` | | `α '+n` | `α` | Line -n within `α` | ### 2.2.2 The Sequential Operator `;` The sequential operator `;` transforms a selection function from operating "within" a selection to operating "starting from the end" of that selection. **Note:** It is erroneous to use `;F` in the nullary case. TODO: or are there interesting semantics? **Note:** `;F` is not defined when `F` is one of `B-` . | Function | Semantics for rank 0 `α` | Semantics for rank 1 `α` | |------------|------------------------------|-------------------------------------| | `α ;e` | Empty selection (rank 2) | Empty selection (rank 2) | | `α ;$` | End of buffer | End of buffer (ignores `α`) | | `α ;#n` | Character n after `α` | Character n after end of `α` | | `α ;n` | Line n after `α` | Line n after end of `α` | | `α ;/re/` | First match forward from `α` | First match forward from end of `α` | | `α ;x/re/` | All matches forward from `α` | All matches forward from end of `α` | | `α ;+n` | Line n after `α` | Line n after end of `α` | ### 2.2.3 The Complement Operator `~` The complement operator `~` takes a selection function and produces a new selection function which returns the complement. Naturally this changes rank somewhat, as explained in the below table. | Rank of output of `F` | Rank of output of `~F` | Semantics | |-----------------------|------------------------|------------------------------------------------------------------| | 0 | 2 | The complement of the position its buffer as a selection | | 1 | 2 | The complement of the range in its buffer as a selection | | 2 | 2 | The selection corresponding to the ranges between the selections | | 3 | 3 | The broadcast of `~` to each buffer | # 3. Text Functions ## 3.1 Text Operations Text operations have signature `Selection × Text → Selection`. They modify the editor state (buffer contents) and return the affected Selection. Text operations use [leading axis theory](https://aplwiki.com/wiki/Leading_axis_theory) where buffer names are the leading axis: TODO TODO TODO if `α F β` where `β` is rank 2 but `α` is rank 1, then operation applies once per range in `β`, using the contents of each range as the text argument. | Function | Effect | Return selection | |----------|--------------------------------------------------------------|----------------------| | `.` | No-op | Current selection | | `c` | No-op | Current selection | | `i` | No-op | Current selection | | `a` | No-op | Current selection | | `d` | Delete current selection in | Empty selection | | `w` | Write the current buffer to its associated file. | Current selection | | `\|` | Run current selection as shell commands, replace with output | Selection of outputs | **Note:** `\|` is undefined when the rank of the current selection is 0. | Function | Effect | Return selection | |----------|--------------------------------------------------|---------------------------------------------| | `. τ` | No-op | Current selection in buffers named by `τ` | | `c τ` | No-op | Replace the current selection by `τ` | | `i τ` | No-op | Insert `τ` into the current selection | | `a τ` | No-op | Append `τ` to the current selection | | `d τ` | Delete current selection in buffers named by `τ` | Empty selection in buffers named by `τ` | | `w τ` | Write the buffers named by `τ`. | Current selection in buffers named by `τ` | | `\| τ` | Run `τ` | Return selection of ephemeral buffer output | **Note:** The text argument `τ` may be a string literal or a Selection (which coerces to Text). **Note:** `\|` and `d` are undefined when the rank of `τ` is 0. | Function | Effect | Semantics | |----------|------------------------------------------------------|-----------------------------------------------------------| | `α . τ` | No-op | Current selection in buffer(s) named by `τ` (`α` ignored) | | `α c τ` | No-op | Change (replace) `α` with text `τ` | | `α i τ` | No-op | Insert text `τ` before `α` | | `α a τ` | No-op | Append text `τ` after `α` | | `α d τ` | Delete `α` in buffers named by `τ` | Empty selection in buffers nameb by `τ` | | `α w τ` | Write (concatenation of) `α` in buffers named by `τ` | `α` | | `α \| τ` | Pipe `α` through command `τ` | Return selection of ephemeral output buffer | **Examples:** TODO: throw some combinators in to make this example nicer. ``` | "date" # Run date with no input, returns selection to output /pattern/ c (| "date") # Replace pattern with date command output selection c (selection | "sort") # Pipe selection through sort, replace with result ``` An idiom ``` α {swap i , d} β ``` Evaluates in parallel * `α (swap i) β` which is `β i α` which means "insert at (the start of) β the content from α" * `α d β` which means "delete α" The net result of this is to _move_ the content of α to (the start of) the selection given by β.