summaryrefslogtreecommitdiff
path: root/src/interpreter/interpreter.ts
diff options
context:
space:
mode:
Diffstat (limited to 'src/interpreter/interpreter.ts')
-rw-r--r--src/interpreter/interpreter.ts60
1 files changed, 35 insertions, 25 deletions
diff --git a/src/interpreter/interpreter.ts b/src/interpreter/interpreter.ts
index c79a2cf..6580a4f 100644
--- a/src/interpreter/interpreter.ts
+++ b/src/interpreter/interpreter.ts
@@ -11,6 +11,11 @@ export class InvalidLambdaTermError extends Error {}
export class MaxRecursionDepthError extends Error {}
+export type Visitors = Map<
+ string,
+ (term: DebrujinifiedLambdaTerm) => DebrujinifiedLambdaTerm
+>;
+
export type DebrujinAbstraction = {
abstraction: {
param: string;
@@ -28,6 +33,7 @@ export type DebrujinApplication = {
export type DebrujinIndex = { name: string; index: number };
export type DebrujinifiedLambdaTerm =
+ | ((t: DebrujinifiedLambdaTerm) => DebrujinifiedLambdaTerm)
| DebrujinAbstraction
| DebrujinApplication
| DebrujinIndex;
@@ -76,6 +82,10 @@ export const substitute = (
index: number,
withTerm: DebrujinifiedLambdaTerm,
): DebrujinifiedLambdaTerm => {
+ if (typeof inTerm === "function") {
+ return inTerm(withTerm);
+ }
+
if ("index" in inTerm) {
if (inTerm.index > index) {
return adjustIndices(inTerm, -1);
@@ -154,51 +164,45 @@ export const adjustIndices = (
export const betaReduce = (
term: DebrujinifiedLambdaTerm,
+ visitors: Visitors,
maxDepth: number,
): DebrujinifiedLambdaTerm => {
if (maxDepth === 0) {
throw new MaxRecursionDepthError("max recursion depth identified");
}
- if ("index" in term) {
+ if (typeof term === "function") {
return term;
}
+ if ("index" in term) {
+ const replacement = visitors.get(term.name)?.apply(null, [term]);
+ return replacement ?? term;
+ }
+
if ("abstraction" in term) {
const { body, param } = term.abstraction;
return {
abstraction: {
- body: betaReduce(body, maxDepth - 1),
+ body: betaReduce(body, visitors, maxDepth - 1),
param,
},
};
}
if ("application" in term) {
- const { left } = term.application;
- const args = term.application.args.map((term) =>
- betaReduce(term, maxDepth - 1),
+ const { left, args } = term.application;
+ const [reducedLeft, ...reducedArgs] = [left, ...args].map((term) =>
+ betaReduce(term, visitors, maxDepth - 1),
);
-
- return args.reduce((acc: DebrujinifiedLambdaTerm, x) => {
+ return reducedArgs.reduce((acc: DebrujinifiedLambdaTerm, x) => {
if ("abstraction" in acc) {
const { body } = acc.abstraction;
- const newBody = substitute(body, 1, x);
- return newBody;
+ const substituted = substitute(body, 1, x);
+ return substituted;
}
- if ("application" in acc) {
- const {
- application: { left, args },
- } = acc;
- return {
- application: {
- left,
- args: [...args, x],
- },
- };
- }
- return { application: { left: acc, args: [x] } };
- }, left);
+ return acc;
+ }, reducedLeft);
}
throw new InvalidLambdaTermError(
@@ -207,6 +211,10 @@ export const betaReduce = (
};
export const emitDebrujin = (term: DebrujinifiedLambdaTerm): string => {
+ if (typeof term === "function") {
+ return term.toString();
+ }
+
if ("index" in term) {
return term.index.toString();
}
@@ -250,18 +258,20 @@ export const interpret = (
term: string,
symbolTable = new SymbolTable(),
allowUnderscores = false, // in our world, underscores should be internal to the game.
- maxDepth = 15,
+ visitors: Visitors = new Map(),
+ maxDepth = 20,
): DebrujinifiedLambdaTerm => {
const ast = parse(term, allowUnderscores);
const debrujined = debrujinify(ast, symbolTable);
let prev = debrujined;
- let next = betaReduce(prev, maxDepth);
+ let next = betaReduce(prev, visitors, maxDepth);
while (emitDebrujin(prev) !== emitDebrujin(next)) {
// alpha equivalence
prev = next;
- next = betaReduce(prev, maxDepth);
+ next = betaReduce(prev, visitors, maxDepth);
}
+ console.log(next);
return next;
};