Add unless command
This commit is contained in:
5
HELP.md
5
HELP.md
@@ -388,6 +388,11 @@ Execute the given lambda if `cond` is truthy. Cond may be a lambda, in which cas
|
||||
A value is truthy UNLESS it is the literal empty string (`''`) or the integer `0`.
|
||||
Example: `foo` -> `if (contains foo) (suffix bar)` -> `foobar`
|
||||
|
||||
#### `unless <cond> <lambda>`
|
||||
Inversion of `if`.
|
||||
Example: `foo` -> `unless (contains foo) (suffix bar)` -> `foo`
|
||||
|
||||
|
||||
|
||||
### Misc
|
||||
|
||||
|
||||
@@ -55,6 +55,7 @@ import {Take} from "./take.js";
|
||||
import {Group} from "./group.js";
|
||||
import {Flatten} from "./flatten.js";
|
||||
import {If} from "./if.js";
|
||||
import {Unless} from "./unless.js";
|
||||
|
||||
export type Commands = Command<CommandData>[]
|
||||
export const commands: Commands = [
|
||||
@@ -109,6 +110,7 @@ export const commands: Commands = [
|
||||
new Trim,
|
||||
new Undo,
|
||||
new Unique,
|
||||
new Unless,
|
||||
new Unquote,
|
||||
new Upper,
|
||||
new Word,
|
||||
|
||||
56
src/vm/commands/unless.ts
Normal file
56
src/vm/commands/unless.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
import {LexInput} from "../lexer.js";
|
||||
import {Command, ParseContext, StrTerm} from "./command.js";
|
||||
import {StrVM} from "../vm.js";
|
||||
import {Call} from "./call.js";
|
||||
|
||||
export type UnlessData = {
|
||||
cond: StrTerm,
|
||||
callable: StrTerm,
|
||||
}
|
||||
|
||||
export class Unless extends Command<UnlessData> {
|
||||
async attemptParse(context: ParseContext): Promise<UnlessData> {
|
||||
return {
|
||||
cond: await context.popTerm(),
|
||||
callable: await context.popTerm(),
|
||||
}
|
||||
}
|
||||
|
||||
isParseCandidate(token: LexInput): boolean {
|
||||
return this.isKeyword(token, 'unless')
|
||||
}
|
||||
|
||||
getDisplayName(): string {
|
||||
return 'unless'
|
||||
}
|
||||
|
||||
async execute(vm: StrVM, data: UnlessData): Promise<StrVM> {
|
||||
return vm.replaceContextFromChild(async (childVM, ctx) => {
|
||||
let cond = ctx.resolveRequired(data.cond)
|
||||
|
||||
// Unless `cond` is a lambda, then call it before evaluating its truthiness.
|
||||
if ( cond.term === 'lambda' ) {
|
||||
await childVM.runInChild(async (lambdaVM, lambdaCtx) => {
|
||||
await (new Call).execute(lambdaVM, {
|
||||
callable: cond,
|
||||
params: [],
|
||||
})
|
||||
|
||||
cond = lambdaCtx.getSubject()
|
||||
})
|
||||
}
|
||||
|
||||
if ( (cond.term !== 'string' && cond.term !== 'int') || cond.value ) {
|
||||
// The string was truthy, so do not execute the body.
|
||||
return
|
||||
}
|
||||
|
||||
const callable = ctx.resolveLambda(data.callable)
|
||||
|
||||
await (new Call).execute(childVM, {
|
||||
callable,
|
||||
params: [],
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user