diff --git a/HELP.md b/HELP.md new file mode 100644 index 0000000..66215dc --- /dev/null +++ b/HELP.md @@ -0,0 +1,356 @@ +`str` : An interactive string manipulation environment + +Copyright (C) 2026 Garrett Mills + +## Syntax Overview + +String manipulation operations are performed on a "subject" -- a given string you are working on. + +Most of the time, you do this by pasting a string into the interpreter, then running a series of +statements that make some changes to the string. Here's an example where a few lines of a TSV file +have been pasted in as the subject: + +```text +str %> paste +┌─────────────── +│ 0 │Mazda RX4 21 6 160 110 3.9 2.62 16.46 0 1 4 4 +│ 1 │Mazda RX4 Wag 21 6 160 110 3.9 2.875 17.02 0 1 4 4 +│ 2 │Datsun 710 22.8 4 108 93 3.85 2.32 18.61 1 1 4 1 +│ 3 │Hornet 4 Drive 21.4 6 258 110 3.08 3.215 19.44 1 0 3 1 +│ 4 │Hornet Sportabout 18.7 8 360 175 3.15 3.44 17.02 0 0 3 2 +├─────────────── +│ :: string +└─────────────── +str %> +``` + +Statements in `str` take the form of ` [...args]`. For a trivial example, here we use a +command to replace all instances of `Hornet` with `Foobar`: + +```text +str %> replace Hornet Foobar +┌─────────────── +│ 0 │Mazda RX4 21 6 160 110 3.9 2.62 16.46 0 1 4 4 +│ 1 │Mazda RX4 Wag 21 6 160 110 3.9 2.875 17.02 0 1 4 4 +│ 2 │Datsun 710 22.8 4 108 93 3.85 2.32 18.61 1 1 4 1 +│ 3 │Foobar 4 Drive 21.4 6 258 110 3.08 3.215 19.44 1 0 3 1 +│ 4 │Foobar Sportabout 18.7 8 360 175 3.15 3.44 17.02 0 0 3 2 +├─────────────── +│ :: string +└─────────────── +str %> +``` + +Notice that I did not have to quote the strings `Hornet` and `Foobar` because they do not contain any +special syntax characters. The only special syntax characters in `str` are single quotes themselves (`'`), +whitespace, and opening parens (`(`). + +Commands are separated either by newlines (pressing enter), or using the `;` separator. For example: + +```text +┌─────────────── +│ 0 │foo bar baz +├─────────────── +│ :: string +└─────────────── +str %> replace oo OO; replace ba BA +┌─────────────── +│ 0 │fOO BAr BAz +├─────────────── +│ :: string +└─────────────── +str %> +``` + + +## Data Types + +- `:: string` - Strings. Does what it says on the tin. +- `:: int` - Base-10 integers (e.g. for use as offsets). +- `:: destructured` - A list split by some value (e.g. `foo::bar` split on `::`). + - These differ from normal lists in that they keep track of their "connective tissue". + - For example, `foo::bar` split on `::` will give you `[foo, bar]`, but will remember that `bar` is prefixed with `::` + - This allows you to `join` the destructured back together later on +- `:: ... :: ()` - Lambdas. Multi-parameter functions. See "Working with Lambdas" below. + + +## Available Commands + +### I/O & Editor Control + +#### `exit` +Stop `str` and exit. This can also be done with `^C`. + +#### `paste` +Replace the current subject with the current contents of your clipboard. + +#### `copy` +Copy the current subject to your clipboard. + +#### `infile ` +Replace the current subject with the contents of the given file. +Example: `infile ~/foo.txt` + +#### `outfile ` +Write the current subject to the given file (replacing its current contents). +Example: `outfile ~/foo.txt` + +#### `assign ` / `= ` +Replace the current context with the given value. +Example: `= 'foo bar'` -> `foo bar` +Example: `= $a` -> (value of `$a`) + +#### `clear` +Clear the current subject (replace it with an empty string). + +#### `show` +Print the current subject. This is done by default after every operation. + +#### `undo` +Undo the last statement/operation. + +#### `redo` +Redo the last undone statement/operation. + +#### `history` +Show a log of recent statements/operations. + +#### `edit` +Open the current subject in an external text editor (specified by `$EDITOR`). +Once you save and close the editor, the modified contents will be loaded back into `str`. +A crutch. + +#### `save []` +Save the current state of the `str` session to the given statefile (default `~/.str.json`). + +#### `load []` +Restore a `str` session from a saved state file (default `~/.str.json`). + +#### `runfile ` +Execute the contents of the given file as a series of `str` commands. + + +### Basic String Operations + +#### `enclose []` +Wrap the string on either side with the given string (default = `(`). +If the string is one half of a matching pair (e.g. `(`, `[`, `{`, &c), surround with the matching pair. +Example: `foo` -> `enclose [` -> `[foo]` + +#### `lower` +Convert the string to lower-case. +Example: `FoO` -> `lower` -> `foo` + +#### `upper` +Convert the string to upper-case. +Example: `FoO` -> `upper` -> `FOO` + +#### `lsub []` +Take a left-substring starting at the given `offset`. Stop after the given `length`, if provided. +Example: `abcdef` -> `lsub 1 3` -> `bcd` + +#### `rsub []` +Take a right-substring starting at the given `offset`. Stop after the given `length`, if provided. +Example: `abcdef` -> `rsub 1 3` -> `cde` + +#### `prefix ` +Prefix the current subject with the given string. +Example: `bar` -> `prefix foo` -> `foobar` + +#### `suffix ` +Suffix the current subject with the given string. +Example: `foo` -> `suffix bar` -> `foobar` + +#### `quote []` +Quote the string with the given quotemark (default `'`). +If the string is already quoted with a standard quotemark, strip it off first. +Example: `'bar'` -> `quote "` -> `"bar"` + +#### `unquote []` +Unquote the string if it is quoted with a standard quotemark (or `with`, if provided explicitly). +Example: `"foo"` -> `unquote` -> `foo` + +#### `replace ` +Replace all instances of `find` with the given `with`. +Example: `foobaz` -> `replace baz bar` -> `foobar` + +#### `rev` +Reverse the given string (or destructured list). +Example: `abcdef` -> `rev` -> `fedcba` + +#### `trim [] []` +Trim leading/trailing instances of `char` from the subject (by default, any whitespace). +By default, `trim` uses the `both` type, but supports the following modes: + +- `start`/`left` - only from the start of the string +- `end`/`right` - only from the end of the string +- `both` - from both start and end of the string +- `lines` - remove any empty lines + +Example: ` foo ` -> `trim left` -> `foo ` +Example: `fffubarfff` -> `trim both f` -> `ubar` + +#### `indent []` +Reindent the current subject with tabs or spaces. +If a `level` is provided, adjust the indentation to that level. +Example: ` word` -> `indent tab` -> `\tword` + +#### `concat [...]` +Replace the current subject with the result of concatenating the provided strings together. +Mostly useful in conjunction with variables. +Example: `concat foo bar` -> `foobar` + + +### Multi-part Manipulation (line-wise, word-wise, destructured) + +#### `line ...` +Execute the given `subcommand` on every line of the subject. +Example: `foo\nbar` -> `line prefix --` -> `--foo\n--bar` + +#### `word ...` +Execute the given `subcommand` on every word of the subject, preserving whitespace. +Example: `foo bar baz` -> `word prefix --` -> `--foo --bar --baz` + +#### `each ...` +Execute the given `subcommand` on every part of a destructured subject. +Example: `[foo, bar, baz]` -> `each prefix A` -> `[Afoo, Abar, Abaz]` + +#### `on ...` +Execute the given subcommand on the specified word/line/index from the current subject. +`word` and `line` apply to strings that have not been destructured. +`index` is applied for destructured subjects. For destructured subjects you may omit the type (e.g. `on 4 ...`). +Example: `foo bar baz` -> `on word 1 rsub 1` -> `foo ba baz` + +#### `drop ` +Delete the specified word/line/index from the current subject. +`word` and `line` apply to strings that have not been destructured. +`index` is applied for destructured subjects. For destructured subjects you may omit the type (e.g. `drop 4`). +Example: `foo bar baz` -> `drop word 1` -> `foo baz` + +#### `contains ` +If the subject contains the given substring, keep it. Otherwise, replace it with an empty string. +Most often used in conjunction with `line`, `word`, or `each` for filtering. +Example: `afoo \n bfoo \n cbar` -> `line contains foo` -> `afoo \n bfoo \n ` + +#### `missing ` +If the subject contains the given substring, replace it with an empty string. Otherwise, keep it. +Most often used in conjunction with `line`, `word`, or `each` for filtering. +Example: `afoo \n bfoo \n cbar` -> `line missing foo` -> `\n \n cbar` + +#### `lines` +Destructure the current subject into individual lines. +Example: `foo \n bar` -> `lines` -> `[foo, bar]` + +#### `words` +Destructure the current subject into individual words. +Example: `foo bar \n baz` -> `words` -> `[foo, bar, baz]` + +#### `split []` +Destructure the current subject based on the given delimiter. +If a `limit` is provided, split the subject no more than the given number of times. +Example: `foo::bar::baz::ban` -> `split :: 2` -> `[foo, bar, baz::ban]` + +#### `chunk ` +Destructure the current subject into chunks based on `every` Nth line/word/character. +Example: `a b c d e f` -> `chunk 2 word` -> `[a b, c d, e f]` + +#### `join []` +Join the current destructured subject back together using the given delimiter. +If no delimiter is provided, it will preserve the existing delimiters between substrings. +If string is not destructured, joins the lines in the string. +Example: `[foo, bar]` -> `join ::` -> `foo::bar` + +#### `sort []` +Sort the items in the destructured subject alphabetically either `asc`ending (default) or `desc`ending. +If string is not destructured, sorts the lines in the string. +Example: `[foo, bar]` -> `sort` -> `[bar, foo]` + +#### `unique` +Filter out duplicate values from the destructured subject. +If string is not destructured, filters out duplicate lines in the string. +Example: `[foo, bar, foo]` -> `unique` -> `[foo, bar]` + +#### `zip ` +Interleave the current destructured subject with the given other destructured subject. +Example: Say `$a` is `[1, 2, 3]` and subject is `[a, b, c]` -> `zip $a` -> `[a, 1, b, 2, c, 3]` + + +### Working with Variables + +Variables in `str` start with a dollar sign and may be alphanumeric with underscores (e.g. `$my_var`). + +Assign a variable using the standard syntax: `$a = 'mystr'` + +You can then use them anywhere you may use a string: `split $a` + +There are also several commands for interactive with variables, defined below. + +#### `to ` +Store the current subject into the given variable. +Example: `foo bar` -> `to $a` + +#### `from ` +Replace the current subject with the value of the given variable. +Example: `from $a` + +#### `set ` +Alternative syntax for variable assignment. +Example: `set $a 'mystr'` is equivalent to `$a = 'mystr'` + +#### `over ...` +Execute the given `subcommand` against the contents of `var` instead of the current subject. +Example: `over $a replace foo bar` + + +### Working with Lambdas (functions, closures, &c) + +`str` supports Lambda functions. These are defined as a series of commands which may optionally +have some number of parameters. For example: + +```text +$myReplacer = (|$a $b| split $a; join $b) +``` + +This trivial lambda takes two parameters, splits the subject on `$a` and joins it back together on `$b` (effectively, `replace`). + +You may omit the parameter list if the lambda takes no parameters. For example, this basic TSV parser: + +```text +$myTSVParser = ( + trim; + trim lines; + lines; + each split \t +) +``` + +#### `call [...]` +Execute the given lambda with the provided parameters, replacing the current context with the result. +Example: `call $myReplacer foo bar` + +`call` operates over the current context. However, you can execute lambdas over the contents of a variable using `over`: + +```text +str %> $a = 'foo bar' +str %> over $a call $myReplacer foo bar +``` + +`call` also supports partial application. For example: + +```text +str %> over $myFooReplacer call $myReplacer foo +``` + +You can then call the partially-applied lambda with the remaining parameter(s): + +```text +str %> call $myFooReplacer bar +``` + + +### Misc + +#### `lipsum ` +Replace the current subject with "Lorem ipsum..." placeholder text. +Can generate individual words, lines, or paragraphs. +Example: `lipsum 4 word` -> `lorem ipsum dolor sit` diff --git a/HELP.txt b/HELP.txt deleted file mode 100644 index a02e2f5..0000000 --- a/HELP.txt +++ /dev/null @@ -1,209 +0,0 @@ -str : An interactive string manipulation environment - Copyright (C) 2025 Garrett Mills - ----------------------------------------- -Input / Output ----------------------------------------- -copy - Copy the current string to the clipboard. (Requires wl-clipboard.) - -paste - Paste the contents of the clipboard to replace the current string. (Requires wl-clipboard.) - -infile - Replace the current string with the contents of . - -outfile - Write the current string as the contents of . - -edit - Open the current string in EDITOR. - -history - Print the undo/redo history - -runfile - Parse a file as a set of str commands and execute them. - -from - Replace the current string with the contents of the variable. - -to - Store the current string contents into the variable. - ----------------------------------------- -String Manipulation ----------------------------------------- -indent [] - Indent the string with the specified number of spaces or tabs. - Default is a single standard indentation level. - -trim [] [] - Remove instances of the given character from either the start/end or both sides of the string. - Default is to trim whitespace from both ends. - -quote [] - Surround the current string in the given quote character. - Default is to use the current "quote" setting, which defaults to a single-quote. - -unquote [] - Try to strip surrounding quotes of the given character from the string. - Will only proceed if the string has the quote mark on both ends. - Default is to try common quote schemes (single/double quotes, backtick). - -enclose [] - Wrap the string in the given character. Tries to match pairs when possible. - Example: Using `(` will wrap with a closing `)` - Default is to wrap with parentheses. - -prefix - Prepend to the current string. - -suffix - Append to the current string. - -split [] - Split the current string using the given separator, and rejoin it using the given separator. - Default is to rejoin on newlines. - -lines [] [] - Like `split`, but defaults to splitting on chunks of whitespace. - -join [] - Join separate lines in the string using the given separator. - -replace [] - Replace all instances of with . - Optionally, define a "range pattern" to replace. Range patterns define - the nth occurrences of that should be replaced. The last number - in a range will be repeated as necessary. - Example: - "First Name Last Name Salutation Age Height" - replace ' ' , [2, 2, 1] - "First Name,Last Name,Salutation,Age,Height" - -lsub [] - Replace the current string with a substring from the left, starting at . - Optionally, limit to characters. - -rsub [] - Like `lsub`, but works on the string from right-to-left. - -reparse - Assuming the string is a valid expression, parse it and convert it to encoding. - Example: reparse json php - ----------------------------------------- -Advanced Manipulation ----------------------------------------- -line - Runs the given command for every line in the string (separated by \n). - -word - Runs the given command for every word in the string (separated by whitespace). - -contains - Check if the current string contains . If not, replace it with an empty string. - -on - Run the given command on the nth line or word. - Example: on line 2 prefix + - -drop - Drops the nth line, word, or element from the subject. - -map [to ] [by ] - Map the subject line-wise or word-wise for the given range. - Default is to map until the end of the string if `to` is not provided, - and to apply to every single line/word. - Example: map line 1 to 5 by 2 prefix + - -over - Apply a command to the given and save it back in . - Example: over $a line prefix + - ----------------------------------------- -State Management ----------------------------------------- -show - Print out the current string. - -clear - Replace the current string with an empty string. - -undo [] - Undo the past operations on the string. Defaults to a single operation. - -redo - Redo the past operations that were undone. Defaults to a single operation. - -save [] - Store the current state of the interpreter to the given file path. - Defaults to ~/.str.json - -load [] - Restore a saved state from the given file path to the interpreter. - Defaults to ~/.str.json -exit - Exit the interpreter. - -set [] - Change the given interpreter to the given . - If no value is given, will reset it back to the default. - Supported settings: - quote (default: ') - What character is used to parse quoted strings. - Also sets the default for the `quote` command. - escape (default: \) - What character is used to parse escape sequences. - session (default: ~/.str.json) - Default file used by `save`/`load` commands. - debug (default: 0) - Set to 1 to enable debug mode - terminator (default: \n) - What character is used to parse sequential commands. - Example: `set terminator ;` will begin parsing commands - separated by a ; instead of when enter is pressed. - ----------------------------------------- -Variables ----------------------------------------- -Define/set a variable - $varname = - Example: $foo = example - $baz = 'another example' - -Use variables as arguments to commands - Example: line prefix $foo - -Use quotes to escape variable names - Example: line prefix '$foo' - -vars - Print the value of all defined variables - -varUnset - Unset a variable. - Example: varUnset $foo - ----------------------------------------- -Functions ----------------------------------------- -function - Start a function definition. Subsequent commands will be parsed - as part of the function body until `end function` is encountered. - - Functions may not be nested. - -end - End a block of the given type. Only supports `function` currently. - -call - Execute the given function. - -Example: -``` -function format_sql_in_clause - line lines - line trim - trim - line quote " - join , - enclose ( -end function -``` diff --git a/src/vm/commands/help.ts b/src/vm/commands/help.ts index 3792908..0f96505 100644 --- a/src/vm/commands/help.ts +++ b/src/vm/commands/help.ts @@ -5,7 +5,7 @@ import {fileURLToPath} from "node:url"; import {StrVM} from "../vm.js"; import fs from "node:fs"; -const helpFile = () => `${dirname(fileURLToPath(import.meta.url))}/../../../HELP.txt` +const helpFile = () => `${dirname(fileURLToPath(import.meta.url))}/../../../HELP.md` const helpContents = () => fs.readFileSync(helpFile(), 'utf8') export class Help extends Command<{}> {