Implement group command + fallback error handling for invalid tables in output
This commit is contained in:
13
HELP.md
13
HELP.md
@@ -272,16 +272,21 @@ 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.
|
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]`
|
Example: `foo::bar::baz::ban` -> `split :: 2` -> `[foo, bar, baz::ban]`
|
||||||
|
|
||||||
#### `chunk <every> <line|word|char>`
|
|
||||||
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 [<on>]`
|
#### `join [<on>]`
|
||||||
Join the current destructured subject back together using the given delimiter.
|
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 no delimiter is provided, it will preserve the existing delimiters between substrings.
|
||||||
If string is not destructured, joins the lines in the string.
|
If string is not destructured, joins the lines in the string.
|
||||||
Example: `[foo, bar]` -> `join ::` -> `foo::bar`
|
Example: `[foo, bar]` -> `join ::` -> `foo::bar`
|
||||||
|
|
||||||
|
#### `chunk <every> <line|word|char>`
|
||||||
|
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]`
|
||||||
|
|
||||||
|
#### `group <callable>`
|
||||||
|
Group the current destructured subject based on some mapped value.
|
||||||
|
The `callable` is called for each destructured part and parts are bucketed together by the output value.
|
||||||
|
Example: `[aa, ab, ba, bb]` -> `group (rsub 1)` => `[[aa, ab], [ba, bb]]`
|
||||||
|
|
||||||
#### `sort [<asc|desc>]`
|
#### `sort [<asc|desc>]`
|
||||||
Sort the items in the destructured subject alphabetically either `asc`ending (default) or `desc`ending.
|
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.
|
If string is not destructured, sorts the lines in the string.
|
||||||
|
|||||||
56
src/vm/commands/group.ts
Normal file
56
src/vm/commands/group.ts
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
import {LexInput} from "../lexer.js";
|
||||||
|
import {Command, hashStrRVal, ParseContext, StrRVal, StrTerm, wrapDestructured} from "./command.js";
|
||||||
|
import {StrVM} from "../vm.js";
|
||||||
|
import {Call} from "./call.js";
|
||||||
|
|
||||||
|
type GroupData = {
|
||||||
|
callable: StrTerm,
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Group extends Command<GroupData> {
|
||||||
|
async attemptParse(context: ParseContext): Promise<GroupData> {
|
||||||
|
return {
|
||||||
|
callable: await context.popTerm(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isParseCandidate(token: LexInput): boolean {
|
||||||
|
return this.isKeyword(token, 'group')
|
||||||
|
}
|
||||||
|
|
||||||
|
getDisplayName(): string {
|
||||||
|
return 'group'
|
||||||
|
}
|
||||||
|
|
||||||
|
async execute(vm: StrVM, data: GroupData): Promise<StrVM> {
|
||||||
|
return vm.replaceContextMatchingTerm(ctx => ({
|
||||||
|
destructured: async sub => {
|
||||||
|
const callable = ctx.resolveLambda(data.callable)
|
||||||
|
const groups: Record<string, StrRVal[]> = {}
|
||||||
|
|
||||||
|
for ( const part of sub ) {
|
||||||
|
// Run the exec in a child VM and group on the hashed result:
|
||||||
|
await vm.runInChild(async (childVM, childCtx) => {
|
||||||
|
await childCtx.replaceSubjectMatchingTerm({ override: part.value })
|
||||||
|
|
||||||
|
await (new Call).execute(childVM, {
|
||||||
|
callable,
|
||||||
|
params: [],
|
||||||
|
})
|
||||||
|
|
||||||
|
const groupVal = hashStrRVal(childCtx.getSubject())
|
||||||
|
groups[groupVal] ??= []
|
||||||
|
groups[groupVal].push(part.value)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const groupValues = Object.values(groups)
|
||||||
|
.map(group =>
|
||||||
|
wrapDestructured(
|
||||||
|
group.map(value => ({ value }))))
|
||||||
|
|
||||||
|
return groupValues.map(value => ({ value }))
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -52,6 +52,7 @@ import {Call} from "./call.js";
|
|||||||
import {Chunk} from "./chunk.js";
|
import {Chunk} from "./chunk.js";
|
||||||
import {Script} from "./script.js";
|
import {Script} from "./script.js";
|
||||||
import {Take} from "./take.js";
|
import {Take} from "./take.js";
|
||||||
|
import {Group} from "./group.js";
|
||||||
|
|
||||||
export type Commands = Command<CommandData>[]
|
export type Commands = Command<CommandData>[]
|
||||||
export const commands: Commands = [
|
export const commands: Commands = [
|
||||||
@@ -68,6 +69,7 @@ export const commands: Commands = [
|
|||||||
new Enclose,
|
new Enclose,
|
||||||
new Exit,
|
new Exit,
|
||||||
new From,
|
new From,
|
||||||
|
new Group,
|
||||||
new Help,
|
new Help,
|
||||||
new History,
|
new History,
|
||||||
new Indent,
|
new Indent,
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import fs from "node:fs";
|
|||||||
import {tempFile} from "../util/fs.js";
|
import {tempFile} from "../util/fs.js";
|
||||||
import {table} from "table";
|
import {table} from "table";
|
||||||
import * as ansi from 'ansis';
|
import * as ansi from 'ansis';
|
||||||
|
import {log} from "../log.js";
|
||||||
|
|
||||||
export const getSubjectDisplay = (sub: StrRVal, prefix: string = '', firstLinePrefix?: string): string => {
|
export const getSubjectDisplay = (sub: StrRVal, prefix: string = '', firstLinePrefix?: string): string => {
|
||||||
if ( typeof firstLinePrefix === 'undefined' ) {
|
if ( typeof firstLinePrefix === 'undefined' ) {
|
||||||
@@ -48,10 +49,15 @@ export const getSubjectDisplay = (sub: StrRVal, prefix: string = '', firstLinePr
|
|||||||
...annotatedTable,
|
...annotatedTable,
|
||||||
]
|
]
|
||||||
|
|
||||||
return table(annotatedTable, config)
|
try {
|
||||||
+ `${prefix}├────────────────────────────────────────────────`
|
return table(annotatedTable, config)
|
||||||
+`\n${prefix}│ :: destructured (:: destructured (:: string))`
|
+ `${prefix}├────────────────────────────────────────────────`
|
||||||
+ `\n${prefix}└────────────────────────────────────────────────`
|
+`\n${prefix}│ :: destructured (:: destructured (:: string))`
|
||||||
|
+ `\n${prefix}└────────────────────────────────────────────────`
|
||||||
|
} catch (e: unknown) {
|
||||||
|
log.error('output', e)
|
||||||
|
// Intentionally fall through to the default output
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let annotated = firstLinePrefix + '┌───────────────'
|
let annotated = firstLinePrefix + '┌───────────────'
|
||||||
|
|||||||
Reference in New Issue
Block a user