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
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}`);
|
|
}
|
|
}
|