Add if command
This commit is contained in:
8
HELP.md
8
HELP.md
@@ -381,6 +381,14 @@ str %> call $myFooReplacer bar
|
|||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### Control Structures
|
||||||
|
|
||||||
|
#### `if <cond> <lambda>`
|
||||||
|
Execute the given lambda if `cond` is truthy. Cond may be a lambda, in which case it is executed.
|
||||||
|
A value is truthy UNLESS it is the literal empty string (`''`) or the integer `0`.
|
||||||
|
Example: `foo` -> `if (contains foo) (suffix bar)` -> `foobar`
|
||||||
|
|
||||||
|
|
||||||
### Misc
|
### Misc
|
||||||
|
|
||||||
#### `lipsum <num> <word|line|para>`
|
#### `lipsum <num> <word|line|para>`
|
||||||
|
|||||||
56
src/vm/commands/if.ts
Normal file
56
src/vm/commands/if.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 IfData = {
|
||||||
|
cond: StrTerm,
|
||||||
|
callable: StrTerm,
|
||||||
|
}
|
||||||
|
|
||||||
|
export class If extends Command<IfData> {
|
||||||
|
async attemptParse(context: ParseContext): Promise<IfData> {
|
||||||
|
return {
|
||||||
|
cond: await context.popTerm(),
|
||||||
|
callable: await context.popTerm(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isParseCandidate(token: LexInput): boolean {
|
||||||
|
return this.isKeyword(token, 'if')
|
||||||
|
}
|
||||||
|
|
||||||
|
getDisplayName(): string {
|
||||||
|
return 'if'
|
||||||
|
}
|
||||||
|
|
||||||
|
async execute(vm: StrVM, data: IfData): Promise<StrVM> {
|
||||||
|
return vm.replaceContextFromChild(async (childVM, ctx) => {
|
||||||
|
let cond = ctx.resolveRequired(data.cond)
|
||||||
|
|
||||||
|
// If `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 empty/falsy, so do not execute the body.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const callable = ctx.resolveLambda(data.callable)
|
||||||
|
|
||||||
|
await (new Call).execute(childVM, {
|
||||||
|
callable,
|
||||||
|
params: [],
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -54,6 +54,7 @@ import {Script} from "./script.js";
|
|||||||
import {Take} from "./take.js";
|
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";
|
||||||
|
|
||||||
export type Commands = Command<CommandData>[]
|
export type Commands = Command<CommandData>[]
|
||||||
export const commands: Commands = [
|
export const commands: Commands = [
|
||||||
@@ -74,6 +75,7 @@ export const commands: Commands = [
|
|||||||
new Group,
|
new Group,
|
||||||
new Help,
|
new Help,
|
||||||
new History,
|
new History,
|
||||||
|
new If,
|
||||||
new Indent,
|
new Indent,
|
||||||
new InFile,
|
new InFile,
|
||||||
new Join,
|
new Join,
|
||||||
|
|||||||
Reference in New Issue
Block a user