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`.
|
A value is truthy UNLESS it is the literal empty string (`''`) or the integer `0`.
|
||||||
Example: `foo` -> `if (contains foo) (suffix bar)` -> `foobar`
|
Example: `foo` -> `if (contains foo) (suffix bar)` -> `foobar`
|
||||||
|
|
||||||
|
#### `unless <cond> <lambda>`
|
||||||
|
Inversion of `if`.
|
||||||
|
Example: `foo` -> `unless (contains foo) (suffix bar)` -> `foo`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### Misc
|
### Misc
|
||||||
|
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ import {Take} from "./take.js";
|
|||||||
import {Group} from "./group.js";
|
import {Group} from "./group.js";
|
||||||
import {Flatten} from "./flatten.js";
|
import {Flatten} from "./flatten.js";
|
||||||
import {If} from "./if.js";
|
import {If} from "./if.js";
|
||||||
|
import {Unless} from "./unless.js";
|
||||||
|
|
||||||
export type Commands = Command<CommandData>[]
|
export type Commands = Command<CommandData>[]
|
||||||
export const commands: Commands = [
|
export const commands: Commands = [
|
||||||
@@ -109,6 +110,7 @@ export const commands: Commands = [
|
|||||||
new Trim,
|
new Trim,
|
||||||
new Undo,
|
new Undo,
|
||||||
new Unique,
|
new Unique,
|
||||||
|
new Unless,
|
||||||
new Unquote,
|
new Unquote,
|
||||||
new Upper,
|
new Upper,
|
||||||
new Word,
|
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