summaryrefslogtreecommitdiff
path: root/repl.ts
blob: 6a67e50a5a00fab896593264775c724d2b88a59f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
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();