You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

65 lines
1.9 KiB
TypeScript

import type { AST } from './ast';
import type { Env } from './env';
import type { Value } from './types';
export function evaluate(ast: AST, env: Env): Value {
switch (ast.kind) {
case 'literal':
return ast.value;
case 'variable':
const val = env.get(ast.name);
if (val === undefined)
throw new Error(`Unknown variable: ${ast.name}`);
return val;
case 'binaryop':
const left = evaluate(ast.left, env);
const right = evaluate(ast.right, env);
return evaluateBinaryOp(ast.operator, left, right);
case 'let':
const val2 = evaluate(ast.value, env);
const newEnv = new Map(env).set(ast.name, val2);
return evaluate(ast.body, newEnv);
case 'if':
const { kind, value } = evaluate(ast.condition, env);
if (kind !== 'bool')
throw new Error('Condition must be bool');
if (value) {
return evaluate(ast.then, env);
} else {
return evaluate(ast.else, env);
}
}
}
function evaluateBinaryOp(op: string, left: Value, right: Value): Value {
const { value: leftValue, kind: leftKind } = left;
const { value: rightValue, kind: rightKind } = right;
const bothNumbers = ((leftKind === 'int' || leftKind === 'float')) && ((rightKind === 'int' || rightKind === 'float'));
if (!bothNumbers)
throw new Error(`Not numbers: ${left}, ${right}`);
switch (op) {
case '+':
return { value: leftValue + rightValue, kind: 'int' };
case '-':
return { value: leftValue - rightValue, kind: 'int' };
case '*':
return { value: leftValue * rightValue, kind: 'int' }
case '/':
return { value: leftValue / rightValue, kind: 'int' }
default:
throw new Error(`Unknown operation: ${op}`);
}
}