summaryrefslogtreecommitdiff
path: root/repl.ts
diff options
context:
space:
mode:
Diffstat (limited to 'repl.ts')
-rw-r--r--repl.ts106
1 files changed, 106 insertions, 0 deletions
diff --git a/repl.ts b/repl.ts
new file mode 100644
index 0000000..6a67e50
--- /dev/null
+++ b/repl.ts
@@ -0,0 +1,106 @@
+const peggy = require("./parser.js");
+
+const isVariable = (ast: any) => typeof ast === "string";
+const isApplication = (ast: any) =>
+ typeof ast === "object" && "application" in ast;
+const isAbstraction = (ast: any) =>
+ typeof ast === "object" && "abstraction" in ast;
+
+const emitCode = (ast: any) => {
+ if (isVariable(ast)) {
+ return ast;
+ }
+
+ if (isApplication(ast)) {
+ const { application } = ast;
+ const { left, right } = application;
+ return `( ${emitCode(left)} ${emitCode(right)} )`;
+ }
+
+ const { abstraction } = ast;
+ const { param, body } = abstraction;
+
+ return `(λ ${emitCode(param)} . ${emitCode(body)})`;
+};
+
+const substitute = (param: string, term: any, result: any) => {
+ if (isVariable(term)) {
+ if (term === param) {
+ return result;
+ }
+ return term;
+ }
+
+ if (isApplication(term)) {
+ const { application } = term;
+ const { left, right } = application;
+
+ return {
+ application: {
+ left: substitute(param, left, result),
+ right: substitute(param, right, result),
+ },
+ };
+ }
+
+ const { abstraction } = term;
+ const { body } = abstraction;
+ return {
+ abstraction: {
+ param: abstraction.param,
+ body: substitute(param, body, result),
+ },
+ };
+};
+
+const betaReduce = (ast: any) => {
+ if (isVariable(ast)) {
+ return ast;
+ }
+
+ if (isApplication(ast)) {
+ const { application } = ast;
+ const { left, right } = application;
+
+ if (isAbstraction(left)) {
+ const { abstraction } = left;
+ const { param } = abstraction;
+
+ return substitute(param, right);
+ }
+
+ return {
+ application: {
+ left: betaReduce(left),
+ right: betaReduce(right),
+ },
+ };
+ }
+
+ // it's an abstraction
+ const { abstraction } = ast;
+ const { param, body } = abstraction;
+ return { param: betaReduce(param), body: betaReduce(body) };
+};
+
+const evaluate = (lambdaTerm: string) => {
+ const ast = peggy.parse(lambdaTerm);
+
+ const fullBetaReduction = betaReduce(ast);
+ return emitCode(fullBetaReduction);
+};
+
+const doRepl = async () => {
+ const prompt = ">>> ";
+ process.stdout.write(prompt);
+
+ for await (const line of console) {
+ const result = evaluate(line);
+ console.log(result);
+ break;
+ }
+
+ await doRepl();
+};
+
+await doRepl();