diff --git a/src/vm/commands/call.ts b/src/vm/commands/call.ts index fc94086..d791279 100644 --- a/src/vm/commands/call.ts +++ b/src/vm/commands/call.ts @@ -1,6 +1,6 @@ import {Command, ParseContext, StrTerm} from "./command.js"; import {LexInput} from "../lexer.js"; -import {StrVM} from "../vm.js"; +import {ExecutionError, StrVM} from "../vm.js"; type CallData = { callable: StrTerm, @@ -32,7 +32,7 @@ export class Call extends Command { async execute(vm: StrVM, data: CallData): Promise { return vm.replaceContextMatchingTerm(ctx => ({ - override: (s) => { + override: async () => { // Resolve the callable and params const callable = ctx.resolveLambda(data.callable) const params = data.params.map(p => ctx.resolveRequired(p)) @@ -61,7 +61,27 @@ export class Call extends Command { } } - return s + if ( params.length > callable.value.params.length ) { + throw new ExecutionError(`Invalid lambda call. Found ${params.length} parameters (expected ${callable.value.params.length})`) + } + + // Create a new child VM to execute the lambda in + return vm.runInChild(async (childVM, childCtx) => { + // Shadow the parameters for the lambda in this scope and bind the values + childCtx.inScope(scope => { + for ( let i = 0; i < params.length; i += 1 ) { + const paramLVal = callable.value.params[i]! + const paramRVal = params[i]! + scope.shadowValue(paramLVal, paramRVal) + } + }) + + for ( const exec of callable.value.body ) { + await exec.command.execute(childVM, exec.data) + } + + return childCtx.getSubject() + }) }, })) }