summaryrefslogtreecommitdiff
path: root/src/interpreter/interpreter.ts
blob: ebd605cdc096da1778f6950a3a6ad09f540831e8 (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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
import {
  type ContinuationExpression,
  type PrimitiveOperationExpression,
  type Program,
  type Value,
} from '@/parser';
import { Environment, type Denotable } from '.';
import {
  BadArgumentError,
  InvalidStateError,
  NotImplementedError,
  type TracingLogger,
} from '@/utils';
import { putBuiltinsOnEnvironemtn } from './builtins';

const evaluateValue = (
  value: Value,
  env: Environment,
  logger: TracingLogger,
): Denotable => {
  if (typeof value === 'string') {
    return { type: 'string', value };
  }

  if ('real' in value) {
    return { type: 'real', value: value.real };
  }
  if ('int' in value) {
    return { type: 'int', value: value.int };
  }
  if ('name' in value) {
    logger.debug(`Evaluating variable: ${value.name}`);
    return env.get(value.name);
  }

  throw new InvalidStateError(`Invalid value: ${value}`);
};

const evaluatePrimitiveOperation = (
  { primitiveOperation }: PrimitiveOperationExpression,
  env: Environment,
  logger: TracingLogger,
) => {
  const { opr, operands, resultBindings, continuations } = primitiveOperation;
  if (operands.length !== 2) {
    throw new BadArgumentError('Primitive operations must have 2 operands');
  }

  const operandValues = operands.map(operand =>
    evaluateValue(operand, env, logger.createChild('evaluteValue')),
  );

  const result = env.apply(opr, operandValues);
  const continuationEnvironment = env.createChild();
  for (const { name } of resultBindings) {
    continuationEnvironment.set(name, result);
  }

  // return the result of the last continuation
  return continuations.reduce((_, continuation, i) => {
    const childLogger = logger.createChild(`continuation[${i}]`);
    return evaluteContinuationExpression(
      continuation,
      continuationEnvironment,
      childLogger,
    );
  }, result);
};

const evaluteContinuationExpression = (
  expr: ContinuationExpression,
  env: Environment,
  logger: TracingLogger,
): Denotable => {
  if ('primitiveOperation' in expr) {
    logger.debug('Evaluating primitive operation');
    return evaluatePrimitiveOperation(
      expr,
      env,
      logger.createChild('evaluatePrimitiveOperation'),
    );
  }

  if ('record' in expr) {
    throw new NotImplementedError('Continuation records are not supported yet');
  }
  if ('select' in expr) {
    throw new NotImplementedError('Continuation select is not supported yet');
  }
  if ('offset' in expr) {
    throw new NotImplementedError('Continuation offset is not supported yet');
  }
  if ('application' in expr) {
    throw new NotImplementedError(
      'Continuation application is not supported yet',
    );
  }
  if ('switch' in expr) {
    throw new NotImplementedError('Continuation switch is not supported yet');
  }
  if ('fix' in expr) {
    throw new NotImplementedError('Continuation fix is not supported yet');
  }

  throw new InvalidStateError(`Invalid continuation expression: ${expr}`);
};

export const evaluate = async (
  ast: Program,
  logger: TracingLogger,
): Promise<Denotable> => {
  const globalEnvironment = putBuiltinsOnEnvironemtn(
    new Environment(logger.createChild('RootEnv')),
  );

  return ast.reduce((_, continuation, i) => {
    const exprLogger = logger.createChild(`statement[${i}]`);
    return evaluteContinuationExpression(
      continuation as ContinuationExpression,
      globalEnvironment,
      exprLogger,
    );
  }, null);
};