Add script command to allow calling external helper scripts

This commit is contained in:
2026-04-09 18:00:59 -05:00
parent 39847fe77a
commit a418f9a89f
3 changed files with 51 additions and 0 deletions

View File

@@ -129,6 +129,10 @@ Restore a `str` session from a saved state file (default `~/.str.json`).
#### `runfile <path>`
Execute the contents of the given file as a series of `str` commands.
#### `script <path> [...<args>]`
Execute an external script and replace the current subject with the returned output.
The current subject is passed as the first argument, followed by the optional additional `args`.
### Basic String Operations

View File

@@ -50,6 +50,7 @@ import {Zip} from "./zip.js";
import {Concat} from "./concat.js";
import {Call} from "./call.js";
import {Chunk} from "./chunk.js";
import {Script} from "./script.js";
export type Commands = Command<CommandData>[]
export const commands: Commands = [
@@ -90,6 +91,7 @@ export const commands: Commands = [
new RSub,
new RunFile,
new Save,
new Script,
new Set,
new Show,
new Sort,

45
src/vm/commands/script.ts Normal file
View File

@@ -0,0 +1,45 @@
import {Command, ParseContext, processPath, StrTerm, unwrapString, wrapString} from "./command.js";
import {LexInput} from "../lexer.js";
import {promisify} from "node:util";
import {execFile} from "node:child_process";
import {StrVM} from "../vm.js";
type ScriptData = {
path: StrTerm,
args: StrTerm[],
}
export class Script extends Command<ScriptData> {
async attemptParse(context: ParseContext): Promise<ScriptData> {
const data: ScriptData = {
path: await context.popTerm(),
args: [],
}
while ( await context.peekTerm() ) {
data.args.push(await context.popTerm())
}
return data
}
getDisplayName(): string {
return 'script'
}
isParseCandidate(token: LexInput): boolean {
return this.isKeyword(token, 'script')
}
async execute(vm: StrVM, data: ScriptData): Promise<StrVM> {
return vm.replaceContextMatchingTerm(ctx => ({
override: async sub => {
const path = processPath(ctx.resolveString(data.path))
const args = data.args.map(arg => ctx.resolveString(arg))
const { stdout, stderr } = await promisify(execFile)(path, [unwrapString(sub), ...args])
return wrapString(stdout)
},
}))
}
}