Implement convert and while + file-based editing
This commit is contained in:
37
src/vm/commands/convert.ts
Normal file
37
src/vm/commands/convert.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import {targets} from "../../util/transliterate.js";
|
||||
import {Command, ParseContext} from "./command.js";
|
||||
import {LexInput} from "../lexer.js";
|
||||
import {Awaitable} from "../../util/types.js";
|
||||
import {StrVM} from "../vm.js";
|
||||
|
||||
export type ConvertData = {
|
||||
from: keyof (typeof targets),
|
||||
to: keyof (typeof targets),
|
||||
}
|
||||
|
||||
export class Convert extends Command<ConvertData> {
|
||||
attemptParse(context: ParseContext): Awaitable<ConvertData> {
|
||||
return {
|
||||
from: context.popKeywordInSet(Object.keys(targets)).value as keyof (typeof targets),
|
||||
to: context.popKeywordInSet(Object.keys(targets)).value as keyof (typeof targets),
|
||||
}
|
||||
}
|
||||
|
||||
isParseCandidate(token: LexInput): boolean {
|
||||
return this.isKeyword(token, 'convert')
|
||||
}
|
||||
|
||||
getDisplayName(): string {
|
||||
return 'convert'
|
||||
}
|
||||
|
||||
async execute(vm: StrVM, data: ConvertData): Promise<StrVM> {
|
||||
return vm.replaceContextMatchingTerm({
|
||||
string: async val => {
|
||||
const from = targets[data.from]
|
||||
const to = targets[data.to]
|
||||
return to.toTarget(await from.fromTarget(val))
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -56,6 +56,8 @@ import {Group} from "./group.js";
|
||||
import {Flatten} from "./flatten.js";
|
||||
import {If} from "./if.js";
|
||||
import {Unless} from "./unless.js";
|
||||
import {Convert} from "./convert.js";
|
||||
import {While} from "./while.js";
|
||||
|
||||
export type Commands = Command<CommandData>[]
|
||||
export const commands: Commands = [
|
||||
@@ -65,6 +67,7 @@ export const commands: Commands = [
|
||||
new Clear,
|
||||
new Concat,
|
||||
new Contains,
|
||||
new Convert,
|
||||
new Copy,
|
||||
new Drop,
|
||||
new Each,
|
||||
@@ -113,6 +116,7 @@ export const commands: Commands = [
|
||||
new Unless,
|
||||
new Unquote,
|
||||
new Upper,
|
||||
new While,
|
||||
new Word,
|
||||
new Words,
|
||||
new Zip,
|
||||
|
||||
57
src/vm/commands/while.ts
Normal file
57
src/vm/commands/while.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
import {LexInput} from "../lexer.js";
|
||||
import {Command, ParseContext, StrTerm} from "./command.js";
|
||||
import {ExecutionContext, StrVM} from "../vm.js";
|
||||
import {Call} from "./call.js";
|
||||
|
||||
export type WhileData = {
|
||||
cond: StrTerm,
|
||||
callable: StrTerm,
|
||||
}
|
||||
|
||||
export class While extends Command<WhileData> {
|
||||
async attemptParse(context: ParseContext): Promise<WhileData> {
|
||||
return {
|
||||
cond: await context.popTerm(),
|
||||
callable: await context.popTerm(),
|
||||
}
|
||||
}
|
||||
|
||||
isParseCandidate(token: LexInput): boolean {
|
||||
return this.isKeyword(token, 'while')
|
||||
}
|
||||
|
||||
getDisplayName(): string {
|
||||
return 'while'
|
||||
}
|
||||
|
||||
async execute(vm: StrVM, data: WhileData): Promise<StrVM> {
|
||||
return vm.replaceContextFromChild(async (childVM, ctx) => {
|
||||
while ( await this.evalCond(childVM, ctx, data) ) {
|
||||
const callable = ctx.resolveLambda(data.callable)
|
||||
|
||||
await (new Call).execute(childVM, {
|
||||
callable,
|
||||
params: [],
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private async evalCond(childVM: StrVM, ctx: ExecutionContext, data: WhileData): Promise<boolean> {
|
||||
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()
|
||||
})
|
||||
}
|
||||
|
||||
return !((cond.term === 'string' || cond.term === 'int') && !cond.value);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user