Implement convert and while + file-based editing

This commit is contained in:
2026-05-28 09:37:45 -05:00
parent 1d20aa59d1
commit afd99d7dfd
8 changed files with 238 additions and 2 deletions

View 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))
},
})
}
}

View File

@@ -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
View 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);
}
}